diff options
Diffstat (limited to '.config/awesome')
86 files changed, 3216 insertions, 1729 deletions
diff --git a/.config/awesome/assets/phosphor/arrow-clockwise-bold.svg b/.config/awesome/assets/phosphor/arrow-clockwise-bold.svg new file mode 100644 index 0000000..142f27a --- /dev/null +++ b/.config/awesome/assets/phosphor/arrow-clockwise-bold.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="#000000" viewBox="0 0 256 256"><path d="M244,56v48a12,12,0,0,1-12,12H184a12,12,0,1,1,0-24H201.1l-19-17.38c-.13-.12-.26-.24-.38-.37A76,76,0,1,0,127,204h1a75.53,75.53,0,0,0,52.15-20.72,12,12,0,0,1,16.49,17.45A99.45,99.45,0,0,1,128,228h-1.37A100,100,0,1,1,198.51,57.06L220,76.72V56a12,12,0,0,1,24,0Z"></path></svg>
\ No newline at end of file diff --git a/.config/awesome/assets/phosphor/arrow-clockwise-fill.svg b/.config/awesome/assets/phosphor/arrow-clockwise-fill.svg new file mode 100644 index 0000000..b4eda78 --- /dev/null +++ b/.config/awesome/assets/phosphor/arrow-clockwise-fill.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="#000000" viewBox="0 0 256 256"><path d="M240,56v48a8,8,0,0,1-8,8H184a8,8,0,0,1-5.66-13.66l17-17-10.55-9.65-.25-.24a80,80,0,1,0-1.67,114.78,8,8,0,1,1,11,11.63A95.44,95.44,0,0,1,128,224h-1.32A96,96,0,1,1,195.75,60l10.93,10L226.34,50.3A8,8,0,0,1,240,56Z"></path></svg>
\ No newline at end of file diff --git a/.config/awesome/assets/phosphor/arrows-in-simple-fill.svg b/.config/awesome/assets/phosphor/arrows-in-simple-fill.svg new file mode 100644 index 0000000..ca722d7 --- /dev/null +++ b/.config/awesome/assets/phosphor/arrows-in-simple-fill.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="#000000" viewBox="0 0 256 256"><path d="M120,144v48a8,8,0,0,1-13.66,5.66L88,179.31,53.66,213.66a8,8,0,0,1-11.32-11.32L76.69,168,58.34,149.66A8,8,0,0,1,64,136h48A8,8,0,0,1,120,144ZM213.66,42.34a8,8,0,0,0-11.32,0L168,76.69,149.66,58.34A8,8,0,0,0,136,64v48a8,8,0,0,0,8,8h48a8,8,0,0,0,5.66-13.66L179.31,88l34.35-34.34A8,8,0,0,0,213.66,42.34Z"></path></svg>
\ No newline at end of file diff --git a/.config/awesome/assets/phosphor/arrows-out-simple-fill.svg b/.config/awesome/assets/phosphor/arrows-out-simple-fill.svg new file mode 100644 index 0000000..81816a9 --- /dev/null +++ b/.config/awesome/assets/phosphor/arrows-out-simple-fill.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="#000000" viewBox="0 0 256 256"><path d="M117.66,138.34a8,8,0,0,1,0,11.32L83.31,184l18.35,18.34A8,8,0,0,1,96,216H48a8,8,0,0,1-8-8V160a8,8,0,0,1,13.66-5.66L72,172.69l34.34-34.35A8,8,0,0,1,117.66,138.34ZM208,40H160a8,8,0,0,0-5.66,13.66L172.69,72l-34.35,34.34a8,8,0,0,0,11.32,11.32L184,83.31l18.34,18.35A8,8,0,0,0,216,96V48A8,8,0,0,0,208,40Z"></path></svg>
\ No newline at end of file diff --git a/.config/awesome/assets/phosphor/caret-left-bold.svg b/.config/awesome/assets/phosphor/caret-left-bold.svg new file mode 100644 index 0000000..413da83 --- /dev/null +++ b/.config/awesome/assets/phosphor/caret-left-bold.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M168.49,199.51a12,12,0,0,1-17,17l-80-80a12,12,0,0,1,0-17l80-80a12,12,0,0,1,17,17L97,128Z"></path></svg> diff --git a/.config/awesome/assets/phosphor/caret-right-bold.svg b/.config/awesome/assets/phosphor/caret-right-bold.svg new file mode 100644 index 0000000..ba52f2b --- /dev/null +++ b/.config/awesome/assets/phosphor/caret-right-bold.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M184.49,136.49l-80,80a12,12,0,0,1-17-17L159,128,87.51,56.49a12,12,0,1,1,17-17l80,80A12,12,0,0,1,184.49,136.49Z"></path></svg> diff --git a/.config/awesome/assets/phosphor/init.lua b/.config/awesome/assets/phosphor/init.lua index d17eae3..421ceb2 100644 --- a/.config/awesome/assets/phosphor/init.lua +++ b/.config/awesome/assets/phosphor/init.lua @@ -1,11 +1,12 @@ local gfs = require "gears.filesystem" local qfs = require "quarrel.fs" +---@type table<string, string> local icons = {} local phosphor_dir = gfs.get_configuration_dir() .. "assets/phosphor/" for _, icon in ipairs(qfs.ls_files(phosphor_dir)) do - if icon:match(".+%.(.+)") == "svg" then + if icon:match ".+%.(.+)" == "svg" then icons[icon:match("(.+)%..+"):gsub("-", "_")] = phosphor_dir .. icon end end diff --git a/.config/awesome/assets/phosphor/lock-simple-fill.svg b/.config/awesome/assets/phosphor/lock-simple-fill.svg new file mode 100644 index 0000000..55f6ef5 --- /dev/null +++ b/.config/awesome/assets/phosphor/lock-simple-fill.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="#000000" viewBox="0 0 256 256"><path d="M208,80H176V56a48,48,0,0,0-96,0V80H48A16,16,0,0,0,32,96V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V96A16,16,0,0,0,208,80ZM96,56a32,32,0,0,1,64,0V80H96Z"></path></svg>
\ No newline at end of file diff --git a/.config/awesome/assets/phosphor/power-bold.svg b/.config/awesome/assets/phosphor/power-bold.svg new file mode 100644 index 0000000..68d670d --- /dev/null +++ b/.config/awesome/assets/phosphor/power-bold.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="#000000" viewBox="0 0 256 256"><path d="M116,128V48a12,12,0,0,1,24,0v80a12,12,0,0,1-24,0Zm66.55-82a12,12,0,0,0-13.1,20.1C191.41,80.37,204,103,204,128a76,76,0,0,1-152,0c0-25,12.59-47.63,34.55-61.95A12,12,0,0,0,73.45,46C44.56,64.78,28,94.69,28,128a100,100,0,0,0,200,0C228,94.69,211.44,64.78,182.55,46Z"></path></svg>
\ No newline at end of file diff --git a/.config/awesome/assets/phosphor/power-fill.svg b/.config/awesome/assets/phosphor/power-fill.svg new file mode 100644 index 0000000..3925fa5 --- /dev/null +++ b/.config/awesome/assets/phosphor/power-fill.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="#000000" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104,104,0,0,0,128,24Zm-8,40a8,8,0,0,1,16,0v64a8,8,0,0,1-16,0Zm8,144A80,80,0,0,1,83.55,61.48a8,8,0,1,1,8.9,13.29,64,64,0,1,0,71.1,0,8,8,0,1,1,8.9-13.29A80,80,0,0,1,128,208Z"></path></svg>
\ No newline at end of file diff --git a/.config/awesome/assets/phosphor/sign-out-bold.svg b/.config/awesome/assets/phosphor/sign-out-bold.svg new file mode 100644 index 0000000..b88c37e --- /dev/null +++ b/.config/awesome/assets/phosphor/sign-out-bold.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="#000000" viewBox="0 0 256 256"><path d="M116,216a12,12,0,0,1-12,12H48a20,20,0,0,1-20-20V48A20,20,0,0,1,48,28h56a12,12,0,0,1,0,24H52V204h52A12,12,0,0,1,116,216Zm108.49-96.49-40-40a12,12,0,0,0-17,17L187,116H104a12,12,0,0,0,0,24h83l-19.52,19.51a12,12,0,0,0,17,17l40-40A12,12,0,0,0,224.49,119.51Z"></path></svg>
\ No newline at end of file diff --git a/.config/awesome/assets/phosphor/sign-out-fill.svg b/.config/awesome/assets/phosphor/sign-out-fill.svg new file mode 100644 index 0000000..a4dda66 --- /dev/null +++ b/.config/awesome/assets/phosphor/sign-out-fill.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="#000000" viewBox="0 0 256 256"><path d="M112,216a8,8,0,0,1-8,8H48a16,16,0,0,1-16-16V48A16,16,0,0,1,48,32h56a8,8,0,0,1,0,16H48V208h56A8,8,0,0,1,112,216Zm109.66-93.66-40-40A8,8,0,0,0,168,88v32H104a8,8,0,0,0,0,16h64v32a8,8,0,0,0,13.66,5.66l40-40A8,8,0,0,0,221.66,122.34Z"></path></svg>
\ No newline at end of file diff --git a/.config/awesome/assets/sounds/vixui-acconnect.wav b/.config/awesome/assets/sounds/vixui-acconnect.wav Binary files differnew file mode 100644 index 0000000..3c3a448 --- /dev/null +++ b/.config/awesome/assets/sounds/vixui-acconnect.wav diff --git a/.config/awesome/assets/sounds/vixui-acdisconnect.wav b/.config/awesome/assets/sounds/vixui-acdisconnect.wav Binary files differnew file mode 100644 index 0000000..ef90616 --- /dev/null +++ b/.config/awesome/assets/sounds/vixui-acdisconnect.wav diff --git a/.config/awesome/assets/sounds/vixui-boot.wav b/.config/awesome/assets/sounds/vixui-boot.wav Binary files differnew file mode 100644 index 0000000..38cba2e --- /dev/null +++ b/.config/awesome/assets/sounds/vixui-boot.wav diff --git a/.config/awesome/assets/sounds/vixui-notifa.wav b/.config/awesome/assets/sounds/vixui-notifa.wav Binary files differnew file mode 100644 index 0000000..4e83a18 --- /dev/null +++ b/.config/awesome/assets/sounds/vixui-notifa.wav diff --git a/.config/awesome/assets/sounds/vixui-notifb.wav b/.config/awesome/assets/sounds/vixui-notifb.wav Binary files differnew file mode 100644 index 0000000..4da95ac --- /dev/null +++ b/.config/awesome/assets/sounds/vixui-notifb.wav diff --git a/.config/awesome/assets/sounds/vixui-usbconnect.wav b/.config/awesome/assets/sounds/vixui-usbconnect.wav Binary files differnew file mode 100644 index 0000000..c85a7ec --- /dev/null +++ b/.config/awesome/assets/sounds/vixui-usbconnect.wav diff --git a/.config/awesome/assets/sounds/vixui-usbdisconnect.wav b/.config/awesome/assets/sounds/vixui-usbdisconnect.wav Binary files differnew file mode 100644 index 0000000..fa7ad81 --- /dev/null +++ b/.config/awesome/assets/sounds/vixui-usbdisconnect.wav diff --git a/.config/awesome/curious.lua b/.config/awesome/curious.lua new file mode 100644 index 0000000..3a0bf5d --- /dev/null +++ b/.config/awesome/curious.lua @@ -0,0 +1,100 @@ +local lgi = require "lgi" +local Gio = lgi.Gio +local GObject = lgi.GObject +local GLib = lgi.GLib +local gpcall = require "gears.protected_call" +local gtimer = require "gears.timer" + +local methods = {} + +local interface = Gio.DBusInterfaceInfo { + name = "com.twoexem.delta.Curious", + methods = { + Gio.DBusMethodInfo { + name = "Select", + out_args = { + Gio.DBusArgInfo { + name = "widget_tree", + signature = "av" + } + } + } + } +} + +-- iterate with ipairs, get the length of the array, check if key value from pairs is a number lower than the length and if so, use as a dict key cause it's not gonna work with :add anyway +local function serialize(builder, t) + local array_length = 0 + for i, _ in ipairs(t) do + array_length = i + end + + for k, v in pairs(t) do + local _type = type(v) + local signature + local data + if _type == "number" then + signature = "d" + data = v + elseif _type == "string" then + signature = "s" + data = v + elseif _type == "boolean" then + signature = "b" + data = v + elseif _type == "thread" or _type == "function" or (_type == "userdata" and not getmetatable(v).__tostring) then + local address = tonumber(tostring(v):match("%a+: 0x(%w+)"), 16) + signature = "(s(t))" + data = { _type, address } + elseif _type == "userdata" then + if getmetatable(v).emit_signal then -- One of the properties available on any awesome luaobject + local _, _, content = tostring(v):match("%a+/(.*)") + local stringified = tostring(v) + if content then + stringified = content + end + local type, extra, address = stringified:match("(%a+)(.*): 0x(%w+)") + if extra ~= "" then + extra:sub(2, -2) + signature = "(s(st))" + data = { type, extra, address } + else + signature = "(s(t))" + data = { type, address } + end + else + signature = "(s())" + data = { _type, {}} + end + end + + if type(k) == "number" and k > array_length then + builder:add("v", GLib.Variant(signature, data)) + else + print(type(signature), type(data)) + local variant = GLib.Variant("{sd}", { "test", 4}) + builder:add_value(GLib.Variant("v", variant)) + end + end +end + +function methods.Select(parameters, invocation) + local builder = GLib.VariantBuilder(GLib.VariantType("av")) + serialize(builder, { "test", some_key = "some_value" }) + local tuple_builder = GLib.VariantBuilder(GLib.VariantType("(av)")) + tuple_builder:add_value(builder:_end()) + -- builder + -- invocation:return_value(GLib.Variant("(av)", { builder })) + invocation:return_value(tuple_builder:_end()) +end + +local function handle_call(_, _, _, _, method, parameters, invocation) + if not methods[method] then print("invalid"); return end + gpcall(methods[method], parameters, invocation) +end + +local function acquire(connection) + connection:register_object("/com/twoexem/delta/Curious", interface, GObject.Closure(handle_call)) +end + +local connection = Gio.bus_own_name(Gio.BusType.SESSION, "com.twoexem.delta.Curious", Gio.BusNameOwnerFlags.NONE, GObject.Closure(acquire)) diff --git a/.config/awesome/misc/autostart.lua b/.config/awesome/misc/autostart.lua index 3cd70f9..b089068 100644 --- a/.config/awesome/misc/autostart.lua +++ b/.config/awesome/misc/autostart.lua @@ -1,7 +1,9 @@ local awful = require "awful" local quarrel = require "quarrel" -if quarrel.is_restart() then return end +if quarrel.is_restart() then + return +end local programs = { "picom -b", @@ -11,7 +13,7 @@ local programs = { "wezterm start --class code_term", "firefox", "discord", - "LD_PRELOAD=/usr/lib/spotify-adblock.so spotify" + "LD_PRELOAD=/usr/lib/spotify-adblock.so spotify", } for _, program in ipairs(programs) do diff --git a/.config/awesome/misc/cfg.lua b/.config/awesome/misc/cfg.lua index 05534b1..aceaa08 100644 --- a/.config/awesome/misc/cfg.lua +++ b/.config/awesome/misc/cfg.lua @@ -1,5 +1,4 @@ local awful = require "awful" -local phosphor = require "assets.phosphor" local cfg = { terminal = "wezterm", @@ -7,32 +6,27 @@ local cfg = { { layout = awful.layout.suit.floating, selected = true, - icon = phosphor.house_fill }, { layout = awful.layout.suit.floating, - icon = phosphor.browser_fill }, { layout = awful.layout.suit.tile.left, master_width_factor = 0.7, - icon = phosphor.discord_logo_fill }, { layout = awful.layout.suit.tile.top, master_width_factor = 0.2, - icon = phosphor.spotify_logo_fill }, { layout = awful.layout.suit.tile.right, - master_width_factor = 0.7, - icon = phosphor.code_fill + -- master_width_factor = 0.7, }, { layout = awful.layout.suit.floating, - icon = phosphor.game_controller_fill - } - } + }, + }, + backlight = "/sys/class/backlight/amdgpu_bl1/", } return cfg diff --git a/.config/awesome/misc/keys.lua b/.config/awesome/misc/keys.lua index 646be52..9d994ce 100644 --- a/.config/awesome/misc/keys.lua +++ b/.config/awesome/misc/keys.lua @@ -1,4 +1,5 @@ local awful = require "awful" +local backlight = require "services.backlight" local beautiful = require "beautiful" local cfg = require "misc.cfg" local fresnel = require "ui.fresnel" @@ -6,7 +7,9 @@ local gtimer = require "gears.timer" local insightful = require "ui.insightful" local naughty = require "naughty" local playerctl = require "services.playerctl" +local powermenu = require "ui.powermenu" local qbind = require "quarrel.bind" +local qstore = require "quarrel.store" local qvars = require "quarrel.vars" local recording = { false, "" } @@ -17,11 +20,11 @@ client.connect_signal("request::default_mousebindings", function() triggers = qvars.btns.left, press = function(c) c:activate { - context = "mouse_click" + context = "mouse_click", } end, group = "client", - desc = "raise client" + desc = "raise client", }, qbind:new { mods = qvars.mods.M, @@ -29,11 +32,11 @@ client.connect_signal("request::default_mousebindings", function() press = function(c) c:activate { context = "mouse_click", - action = "mouse_move" + action = "mouse_move", } end, group = "client", - desc = "move client" + desc = "move client", }, qbind:new { mods = qvars.mods.M, @@ -41,12 +44,12 @@ client.connect_signal("request::default_mousebindings", function() press = function(c) c:activate { context = "mouse_click", - action = "mouse_resize" + action = "mouse_resize", } end, group = "client", - desc = "resize client" - } + desc = "resize client", + }, } end) @@ -59,7 +62,7 @@ client.connect_signal("request::default_keybindings", function() c:kill() end, group = "client", - desc = "close" + desc = "close", }, qbind:new { mods = qvars.mods.M, @@ -68,7 +71,7 @@ client.connect_signal("request::default_keybindings", function() c.maximized = not c.maximized end, group = "client", - desc = "(un)maximize" + desc = "(un)maximize", }, qbind:new { mods = qvars.mods.M, @@ -79,7 +82,7 @@ client.connect_signal("request::default_keybindings", function() end) end, group = "client", - desc = "minimize" + desc = "minimize", }, qbind:new { mods = qvars.mods.M, @@ -88,8 +91,8 @@ client.connect_signal("request::default_keybindings", function() c.fullscreen = not c.fullscreen end, group = "client", - desc = "toggle fullscreen" - } + desc = "toggle fullscreen", + }, } end) @@ -99,7 +102,7 @@ awful.keyboard.append_global_keybindings { triggers = "r", press = awesome.restart, group = "awesome", - desc = "restart awesome" + desc = "restart awesome", }, qbind:new { mods = qvars.mods.M, @@ -108,7 +111,7 @@ awful.keyboard.append_global_keybindings { insightful:toggle() end, group = "awesome", - desc = "toggle insightful" + desc = "toggle insightful", }, qbind:new { mods = qvars.mods.M, @@ -117,36 +120,54 @@ awful.keyboard.append_global_keybindings { fresnel:show() end, group = "awesome", - desc = "toggle fresnel" + desc = "toggle fresnel", + }, + qbind:new { + mods = qvars.mods.M, + triggers = "p", + press = function() + qstore.panel_toggle:press() + end, + group = "awesome", + desc = "toggle bar panel", + }, + qbind:new { + mods = qvars.mods.M, + triggers = "l", + press = function() + powermenu:toggle() + end, + group = "awesome", + desc = "toggle powermenu", }, qbind:new { triggers = "XF86AudioMute", press = function() - awful.spawn("wpctl set-mute @DEFAULT_SINK@ toggle") + awful.spawn "wpctl set-mute @DEFAULT_SINK@ toggle" end, group = "audio", - desc = "mute" + desc = "mute", }, qbind:new { triggers = { { "XF86AudioRaiseVolume", true }, - { "XF86AudioLowerVolume", false } + { "XF86AudioLowerVolume", false }, }, press = function(up) if up then - awful.spawn("wpctl set-volume @DEFAULT_SINK@ 5%+") + awful.spawn "wpctl set-volume @DEFAULT_SINK@ 5%+" else - awful.spawn("wpctl set-volume @DEFAULT_SINK@ 5%-") + awful.spawn "wpctl set-volume @DEFAULT_SINK@ 5%-" end end, group = "audio", - desc = "increase/decrease volume" + desc = "increase/decrease volume", }, qbind:new { triggers = { { "XF86AudioNext", true }, - { "XF86AudioPrev", false } + { "XF86AudioPrev", false }, }, press = function(next) if next then @@ -156,7 +177,7 @@ awful.keyboard.append_global_keybindings { end end, group = "audio", - desc = "previous/next song" + desc = "previous/next song", }, qbind:new { triggers = "XF86AudioPlay", @@ -164,23 +185,23 @@ awful.keyboard.append_global_keybindings { playerctl:play_pause() end, group = "audio", - desc = "(un)pause song" + desc = "(un)pause song", }, qbind:new { triggers = { { "XF86MonBrightnessUp", true }, - { "XF86MonBrightnessDown", false } + { "XF86MonBrightnessDown", false }, }, press = function(up) if up then - awful.spawn("brightnessctl set +51") + backlight.set(backlight.get() + 51) else - awful.spawn("brightnessctl set 51-") + backlight.set(backlight.get() - 51) end end, group = "brightness", - desc = "increase/decrease brightness" + desc = "increase/decrease brightness", }, qbind:new { @@ -190,33 +211,49 @@ awful.keyboard.append_global_keybindings { awful.spawn(cfg.terminal) end, group = "launcher", - desc = "launch terminal" + desc = "launch terminal", }, qbind:new { triggers = "Print", press = function() - local date = os.date("%Y%m%d_%H%M%S") - local path = os.getenv("HOME") .. "/Pictures/" .. date .. ".png" + local date = os.date "%Y%m%d_%H%M%S" + local path = os.getenv "HOME" .. "/Pictures/" .. date .. ".png" -- Can't use naughty.notification cause it gets in the screenshot -- Also can't use easy_async_with_shell cause it's buggy - awful.spawn.with_shell("maim --hidecursor " .. path .. " && xclip -selection clipboard -t image/png " .. path .. [[ && notify-send -a "Maim" "Screenshot taken" "Saved at ]] .. path .. [["]]) + awful.spawn.with_shell( + "maim --hidecursor " + .. path + .. " && xclip -selection clipboard -t image/png " + .. path + .. [[ && notify-send -a "Maim" "Screenshot taken" "Saved at ]] + .. path + .. [["]] + ) end, group = "screenshot", - desc = "take fullscreen screenshot" + desc = "take fullscreen screenshot", }, qbind:new { mods = qvars.mods.S, triggers = "Print", press = function() - local date = os.date("%Y%m%d_%H%M%S") - local path = os.getenv("HOME") .. "/Pictures/" .. date .. ".png" + local date = os.date "%Y%m%d_%H%M%S" + local path = os.getenv "HOME" .. "/Pictures/" .. date .. ".png" - awful.spawn.with_shell("maim --hidecursor -s " .. path .. " && xclip -selection clipboard -t image/png " .. path .. [[ && notify-send -a "Maim" "Screenshot taken" "Saved at ]] .. path .. [["]]) + awful.spawn.with_shell( + "maim --hidecursor -s " + .. path + .. " && xclip -selection clipboard -t image/png " + .. path + .. [[ && notify-send -a "Maim" "Screenshot taken" "Saved at ]] + .. path + .. [["]] + ) end, group = "screenshot", - desc = "take region screenshot" + desc = "take region screenshot", }, qbind:new { @@ -224,45 +261,45 @@ awful.keyboard.append_global_keybindings { triggers = "Print", press = function() if recording[1] then - awful.spawn("giph --stop") + awful.spawn "giph --stop" naughty.notification { app_name = "Giph", title = "Recording stopped", - message = "Saved at " .. recording[2] + message = "Saved at " .. recording[2], } recording[1] = false recording[2] = "" else recording[1] = true - recording[2] = os.getenv("HOME") .. "/Videos/" .. os.date("%Y%m%d_%H%M%S") .. ".mp4" + recording[2] = os.getenv "HOME" .. "/Videos/" .. os.date "%Y%m%d_%H%M%S" .. ".mp4" awful.spawn("giph --format mp4 --framerate 30 " .. recording[2]) naughty.notification { app_name = "Giph", - title = "Recording started" + title = "Recording started", } end end, group = "video", - desc = "toggle recording" + desc = "toggle recording", }, qbind:new { mods = qvars.mods.M, triggers = "k", press = function() - awful.spawn("xkblayout-state set +1") + awful.spawn "xkblayout-state set +1" end, group = "keyboard", - desc = "next keyboard layout" + desc = "next keyboard layout", }, qbind:new { mods = qvars.mods.MS, triggers = "k", press = function() - awful.spawn("xkblayout-state set -1") + awful.spawn "xkblayout-state set -1" end, group = "keyboard", - desc = "previous keyboard layout" + desc = "previous keyboard layout", }, qbind:new { @@ -270,14 +307,14 @@ awful.keyboard.append_global_keybindings { triggers = "Up", press = awful.tag.viewprev, group = "tag", - desc = "switch to previous" + desc = "switch to previous", }, qbind:new { mods = qvars.mods.M, triggers = "Down", press = awful.tag.viewnext, group = "tag", - desc = "switch to next" + desc = "switch to next", }, qbind:new { mods = qvars.mods.MC, @@ -287,6 +324,6 @@ awful.keyboard.append_global_keybindings { tag.master_width_factor = cfg.tags[tonumber(tag.name)].master_width_factor or beautiful.master_width_factor end, group = "tag", - desc = "reset master width" - } + desc = "reset master width", + }, } diff --git a/.config/awesome/misc/rules.lua b/.config/awesome/misc/rules.lua index b4d1dfa..92ac596 100644 --- a/.config/awesome/misc/rules.lua +++ b/.config/awesome/misc/rules.lua @@ -3,98 +3,99 @@ local beautiful = require "beautiful" local ruled = require "ruled" ruled.client.connect_signal("request::rules", function() - ruled.client.append_rule({ - id = "global", - rule = {}, - properties = { - border_width = beautiful.border_width, - border_color = beautiful.border_normal, - focus = awful.client.focus.filter, - raise = true, - screen = awful.screen.preferred, - placement = awful.placement.centered+awful.placement.no_offscreen, - floating = false, - honor_padding = true - } - }) + ruled.client.append_rules { + { + id = "global", + rule = {}, + properties = { + border_width = beautiful.border_width, + border_color = beautiful.border_normal, + focus = awful.client.focus.filter, + raise = true, + screen = awful.screen.preferred, + placement = awful.placement.centered + awful.placement.no_offscreen, + floating = false, + honor_padding = true, + }, + }, - ruled.client.append_rule({ - id = "titlebars", - rule_any = { - type = { - "normal", - "dialog" - } + { + id = "titlebars", + rule_any = { + type = { + "normal", + "dialog", + }, + }, + properties = { + titlebars_enabled = true, + }, }, - properties = { - titlebars_enabled = true - } - }) - ruled.client.append_rule({ - id = "pip", - rule = { - name = "Picture-in-Picture" + { + id = "pip", + rule = { + name = "Picture-in-Picture", + }, + properties = { + ontop = true, + }, }, - properties = { - ontop = true - } - }) - ruled.client.append_rule({ - id = "browser_tag", - rule_any = { - instance = { "Navigator" } + { + id = "browser_tag", + rule_any = { + instance = { "Navigator" }, + }, + properties = { + screen = 1, + tag = awful.screen.focused().tags[2], + }, }, - properties = { - screen = 1, - tag = screen[1].tags[2] - } - }) - ruled.client.append_rule({ - id = "chat_tag", - rule_any = { - class = { "discord" } + { + id = "chat_tag", + rule_any = { + class = { "discord" }, + }, + properties = { + screen = 1, + tag = awful.screen.focused().tags[3], + }, }, - properties = { - screen = 1, - tag = screen[1].tags[3] - } - }) - - ruled.client.append_rule({ - id = "music_tag", - rule_any = { - class = { "Spotify" } + { + id = "music_tag", + rule_any = { + class = { "Spotify" }, + }, + properties = { + screen = 1, + tag = awful.screen.focused().tags[4], + titlebars_enabled = false, + }, }, - properties = { - screen = 1, - tag = screen[1].tags[4], - titlebars_enabled = false - } - }) - ruled.client.append_rule({ - id = "code_tag", - rule_any = { - class = { "lite-xl", "code_term" } + { + id = "code_tag", + rule_any = { + class = { "lite-xl", "code_term" }, + }, + properties = { + screen = 1, + tag = awful.screen.focused().tags[5], + }, }, - properties = { - screen = 1, - tag = screen[1].tags[5] - } - }) - ruled.client.append_rule({ - id = "steam", - rule_any = { - class = { "steam" } + { + id = "steam", + rule_any = { + class = { "steam" }, + }, + properties = { + screen = 1, + tag = awful.screen.focused().tags[6], + }, }, - properties = { - screen = 1, - tag = screen[1].tags[6] - } - }) + } end) diff --git a/.config/awesome/prismite.lua b/.config/awesome/prismite.lua index 72701b6..5de089f 100644 --- a/.config/awesome/prismite.lua +++ b/.config/awesome/prismite.lua @@ -27,13 +27,11 @@ theme.border_marked = qvars.colors.bright.black theme.notification_icon_size = dpi(32) theme.notification_border_width = qvars.border_width theme.notification_border_color = theme.border_normal +theme.notification_max_width = qvars.char_width * 48 theme.notification_shape = qvars.shape theme.notification_spacing = theme.useless_gap * 2 - -naughty.config.presets.critical.bg = theme.bg_normal -naughty.config.presets.critical.timeout = 3 -naughty.config.presets.critical.fg = theme.fg_normal -naughty.config.presets.critical.border_color = qvars.colors.red + +naughty.config.defaults.timeout = qvars.notif_timeout naughty.config.defaults.position = "bottom_right" naughty.config.defaults.border_width = qvars.border_width @@ -41,4 +39,3 @@ theme.tasklist_plain_task_name = true theme.enable_spawn_cursor = false return theme - diff --git a/.config/awesome/quarrel/bezier.lua b/.config/awesome/quarrel/bezier.lua new file mode 100644 index 0000000..4229961 --- /dev/null +++ b/.config/awesome/quarrel/bezier.lua @@ -0,0 +1,343 @@ +--------------------------------------------------------------------------- +--- A helper module for computations involving Bézier curves +-- +-- @author Alex Belykh <albel727@ngs.ru> +-- @copyright 2021 Alex Belykh +-- @submodule gears.math +--------------------------------------------------------------------------- + +local table_insert = table.insert + +local bezier = {} + +--- Compute the value of a Bézier curve at a given value of the t parameter. +-- +-- This function evaluates the given curve `B` of an arbitrary degree +-- at a given point t. +-- +-- @tparam {number,...} c The table of control points of the curve. +-- @tparam number t The value of the t parameter to evaluate the curve at. +-- @treturn[1] number The value of `B(t)`. +-- @treturn[2] nil `nil`, if c is empty. +-- @staticfct gears.math.bezier.curve_evaluate_at +-- @see wibox.widget.graph.step_hook +function bezier.curve_evaluate_at(c, t) + local from = c + local tmp = { nil, nil, nil, nil } + while #from > 1 do + for i = 1, #from - 1 do + tmp[i] = from[i] * (1 - t) + from[i + 1] * t + end + tmp[#from] = nil + from = tmp + end + + return from[1] +end + +--- Split a Bézier curve into two curves at a given value of the t parameter. +-- +-- This function splits the given curve `B` of an arbitrary degree at a point t +-- into two curves of the same degree `B_left` and `B_right`, such that +-- `B_left(0)=B(0)`, `B_left(1)=B(t)=B_right(0)`, `B_right(1)=B(1)`. +-- +-- @tparam {number,...} c The table of control points of the curve. +-- @tparam number t The value of the t parameter to split the curve at. +-- @treturn {number,...} The table of control points for `B_left`. +-- @treturn {number,...} The table of control points for `B_right`. +-- @staticfct gears.math.bezier.curve_split_at +-- @see wibox.widget.graph.step_hook +function bezier.curve_split_at(c, t) + local coefs_left, coefs_right = {}, {} + local from = c + local tmp = { nil, nil, nil, nil } + while #from > 0 do + table_insert(coefs_left, from[1]) + table_insert(coefs_right, 1, from[#from]) + for i = 1, #from - 1 do + tmp[i] = from[i] * (1 - t) + from[i + 1] * t + end + tmp[#from] = nil + from = tmp + end + + return coefs_left, coefs_right +end + +--- Get the n-th derivative Bézier curve of a Bézier curve. +-- +-- This function computes control points for the curve that is +-- the derivative of order `n` in `t`, i.e. `B^(n)(t)`, +-- of the given curve `B(t)` of an arbitrary degree. +-- +-- @tparam {number,...} c The table of control points of the curve. +-- @tparam[opt=1] integer n The order of the derivative to take. +-- @treturn[1] {number,...} The table of control points of `B^(n)(t)`. +-- @treturn[2] nil If n is less than 0. +-- @staticfct gears.math.bezier.curve_derivative +-- @see wibox.widget.graph.step_hook +function bezier.curve_derivative(c, n) + n = n or 1 + if n < 0 then + return + end + if n < 1 then + return c + end + local c_len = #c + if c_len < n + 1 then + return {} + end + + local from = c + local tmp = {} + + for l = c_len - 1, c_len - n, -1 do + for i = 1, l do + tmp[i] = (from[i + 1] - from[i]) * l + end + tmp[l + 1] = nil + from = tmp + end + + return from +end + +-- This is used instead of plain 0 to try and be compatible +-- with objects that implement their own arithmetic via metatables. +local function get_zero(c, zero) + return c and c * 0 or zero +end + +--- Compute the value of the n-th derivative of a Bézier curve +--- at a given value of the t parameter. +-- +-- This is roughly the same as +-- `curve_evaluate_at(curve_derivative(c, n), t)`, but the latter +-- would throw errors or return nil instead of 0 in some cases. +-- +-- @tparam {number,...} c The table of control points of the curve. +-- @tparam number t The value of the t parameter to compute the derivative at. +-- @tparam[opt=1] integer n The order of the derivative to take. +-- @tparam[opt=nil] number|nil zero The value to return if c is empty. +-- @treturn[1] number The value of `B^(n)(t)`. +-- @treturn[2] nil nil, if n is less than 0. +-- @treturn[3] number|nil The value of the zero parameter, if c is empty. +-- @staticfct gears.math.bezier.curve_derivative_at +-- @see wibox.widget.graph.step_hook +function bezier.curve_derivative_at(c, t, n, zero) + local d = bezier.curve_derivative(c, n) + if not d then + return + end + + return bezier.curve_evaluate_at(d, t) or get_zero(c[1], zero) +end + +--- Compute the value of the 1-st derivative of a Bézier curve at t=0. +-- +-- This is the same as `curve_derivative_at(c, 0)`, but since it's particularly +-- computationally simple and useful in practice, it has its own function. +-- +-- @tparam {number,...} c The table of control points of the curve. +-- @tparam[opt=nil] number|nil zero The value to return if c is empty. +-- @treturn[1] number The value of `B'(0)`. +-- @treturn[2] number|nil The value of the zero parameter, if c is empty. +-- @staticfct gears.math.bezier.curve_derivative_at_zero +-- @see wibox.widget.graph.step_hook +function bezier.curve_derivative_at_zero(c, zero) + local l = #c + if l < 2 then + return get_zero(c[1], zero) + end + return (c[2] - c[1]) * (l - 1) +end + +--- Compute the value of the 1-st derivative of a Bézier curve at t=1. +-- +-- This is the same as `curve_derivative_at(c, 1)`, but since it's particularly +-- computationally simple and useful in practice, it has its own function. +-- +-- @tparam {number,...} c The table of control points of the curve. +-- @tparam[opt=nil] number|nil zero The value to return if c is empty. +-- @treturn[1] number The value of `B'(1)`. +-- @treturn[2] number|nil The value of the zero parameter, if c is empty. +-- @staticfct gears.math.bezier.curve_derivative_at_one +-- @see wibox.widget.graph.step_hook +function bezier.curve_derivative_at_one(c, zero) + local l = #c + if l < 2 then + return get_zero(c[1], zero) + end + return (c[l] - c[l - 1]) * (l - 1) +end + +--- Get the (n+1)-th degree Bézier curve, that has the same shape as +-- a given n-th degree Bézier curve. +-- +-- Given the control points of a curve B of degree n, this function computes +-- the control points for the curve Q, such that `Q(t) = B(t)`, and +-- Q has the degree n+1, i.e. it has one control point more. +-- +-- @tparam {number,...} c The table of control points of the curve B. +-- @treturn {number,...} The table of control points of the curve Q. +-- @staticfct gears.math.bezier.curve_elevate_degree +-- @see wibox.widget.graph.step_hook +function bezier.curve_elevate_degree(c) + local ret = { c[1] } + local len = #c + + for i = 1, len - 1 do + ret[i + 1] = (i * c[i] + (len - i) * c[i + 1]) / len + end + + ret[len + 1] = c[len] + return ret +end + +--- Get a cubic Bézier curve that passes through given points (up to 4). +-- +-- This function takes up to 4 values and returns the 4 control points +-- for a cubic curve +-- +-- `B(t) = c0\*(1-t)^3 + 3\*c1\*t\*(1-t)^2 + 3\*c2\*t^2\*(1-t) + c3\*t^3`, +-- that takes on these values at equidistant values of the t parameter. +-- +-- If only p0 is given, `B(0)=B(1)=B(for all t)=p0`. +-- +-- If p0 and p1 are given, `B(0)=p0` and `B(1)=p1`. +-- +-- If p0, p1 and p2 are given, `B(0)=p0`, `B(1/2)=p1` and `B(1)=p2`. +-- +-- For 4 points given, `B(0)=p0`, `B(1/3)=p1`, `B(2/3)=p2`, `B(1)=p3`. +-- +-- @tparam number p0 +-- @tparam[opt] number p1 +-- @tparam[opt] number p2 +-- @tparam[opt] number p3 +-- @treturn number c0 +-- @treturn number c1 +-- @treturn number c2 +-- @treturn number c3 +-- @staticfct gears.math.bezier.cubic_through_points +-- @see wibox.widget.graph.step_hook +function bezier.cubic_through_points(p0, p1, p2, p3) + if not p1 then + return p0, p0, p0, p0 + end + if not p2 then + local c1 = (2 * p0 + p1) / 3 + local c2 = (2 * p1 + p0) / 3 + return p0, c1, c2, p1 + end + if not p3 then + local c1 = (4 * p1 - p2) / 3 + local c2 = (4 * p1 - p0) / 3 + return p0, c1, c2, p2 + end + local c1 = (-5 * p0 + 18 * p1 - 9 * p2 + 2 * p3) / 6 + local c2 = (-5 * p3 + 18 * p2 - 9 * p1 + 2 * p0) / 6 + return p0, c1, c2, p3 +end + +--- Get a cubic Bézier curve with given values and derivatives at endpoints. +-- +-- This function computes the 4 control points for the cubic curve B, such that +-- `B(0)=p0`, `B'(0)=d0`, `B(1)=p3`, `B'(1)=d3`. +-- +-- @tparam number d0 The value of the derivative at t=0. +-- @tparam number p0 The value of the curve at t=0. +-- @tparam number p3 The value of the curve at t=1. +-- @tparam number d3 The value of the derivative at t=1. +-- @treturn number c0 +-- @treturn number c1 +-- @treturn number c2 +-- @treturn number c3 +-- @staticfct gears.math.bezier.cubic_from_points_and_derivatives +-- @see wibox.widget.graph.step_hook +function bezier.cubic_from_points_and_derivatives(d0, p0, p3, d3) + local c1 = p0 + d0 / 3 + local c2 = p3 - d3 / 3 + return p0, c1, c2, p3 +end + +--- Get a cubic Bézier curve with given values at endpoints and starting +--- derivative, while minimizing (an approximation of) the stretch energy. +-- +-- This function computes the 4 control points for the cubic curve B, such that +-- `B(0)=p0`, `B'(0)=d0`, `B(1)=p3`, and +-- the integral of `(B'(t))^2` on `t=[0,1]` is minimal. +-- (The actual stretch energy is the integral of `|B'(t)|`) +-- +-- In practical terms this is almost the same as "the curve of shortest length +-- connecting given points and having the given starting speed". +-- +-- @tparam number d0 The value of the derivative at t=0. +-- @tparam number p0 The value of the curve at t=0. +-- @tparam number p3 The value of the curve at t=1. +-- @treturn number c0 +-- @treturn number c1 +-- @treturn number c2 +-- @treturn number c3 +-- @staticfct gears.math.bezier.cubic_from_derivative_and_points_min_stretch +-- @see wibox.widget.graph.step_hook +function bezier.cubic_from_derivative_and_points_min_stretch(d0, p0, p3) + local c1 = p0 + d0 / 3 + local c2 = (2 * p0 - c1 + 3 * p3) / 4 + return p0, c1, c2, p3 +end + +--- Get a cubic Bézier curve with given values at endpoints and starting +--- derivative, while minimizing (an approximation of) the strain energy. +-- +-- This function computes the 4 control points for the cubic curve B, such that +-- `B(0)=p0`, `B'(0)=d0`, `B(1)=p3`, and +-- the integral of `(B''(t))^2` on `t=[0,1]` is minimal. +-- +-- In practical terms this is almost the same as "the curve of smallest +-- speed change connecting given points and having the given starting speed". +-- +-- @tparam number d0 The value of the derivative at t=0. +-- @tparam number p0 The value of the curve at t=0. +-- @tparam number p3 The value of the curve at t=1. +-- @treturn number c0 +-- @treturn number c1 +-- @treturn number c2 +-- @treturn number c3 +-- @staticfct gears.math.bezier.cubic_from_derivative_and_points_min_strain +-- @see wibox.widget.graph.step_hook +function bezier.cubic_from_derivative_and_points_min_strain(d, p0, p3) + local c1 = p0 + d / 3 + local c2 = (c1 + p3) / 2 + return p0, c1, c2, p3 +end + +--- Get a cubic Bézier curve with given values at endpoints and starting +--- derivative, while minimizing the jerk energy. +-- +-- This function computes the 4 control points for the cubic curve B, such that +-- `B(0)=p0`, `B'(0)=d0`, `B(1)=p3`, and +-- the integral of `(B'''(t))^2` on `t=[0,1]` is minimal. +-- +-- In practical terms this is almost the same as "the curve of smallest +-- acceleration change connecting given points and having the given +-- starting speed". +-- +-- @tparam number d0 The value of the derivative at t=0. +-- @tparam number p0 The value of the curve at t=0. +-- @tparam number p3 The value of the curve at t=1. +-- @treturn number c0 +-- @treturn number c1 +-- @treturn number c2 +-- @treturn number c3 +-- @staticfct gears.math.bezier.cubic_from_derivative_and_points_min_jerk +-- @see wibox.widget.graph.step_hook +function bezier.cubic_from_derivative_and_points_min_jerk(d, p0, p3) + local c1 = p0 + d / 3 + local c2 = c1 + (p3 - p0) / 3 + return p0, c1, c2, p3 +end + +return bezier + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/.config/awesome/quarrel/bind.lua b/.config/awesome/quarrel/bind.lua index 4cb5d44..a1abf29 100644 --- a/.config/awesome/quarrel/bind.lua +++ b/.config/awesome/quarrel/bind.lua @@ -2,56 +2,105 @@ local awful = require "awful" local gtable = require "gears.table" local qstore = require "quarrel.store" +---@class QuarrelBind local qbind = {} qstore.bindings = {} -local function get_binding_function(trigger) - if type(trigger) == "number" and trigger <= 5 and trigger > 0 then +---@alias mouse_button +---| 0 Left mouse button +---| 1 Middle mouse click +---| 2 Right mouse button +---| 3 Scroll up +---| 4 Scroll down + +---@alias bind +---| mouse_button +---| string + +---@alias modifier +---| "Mod1" Alt +---| "Mod4" Super +---| "Mod5" AltGr +---| "Shift" Shift +---| "Control" Ctrl + +---@class Trigger +---@field [1] bind +---@field [2] any Value passed to the press callback + +---@class Binding +---@field hidden boolean? Whether the binding shows up in the help menu +---@field mods modifier[]? Modifiers +---@field press fun(...) | fun(any, ...) Function to run when the trigger is pressed +---@field desc string? Description +---@field group string? What group the binding will show up in +---@field triggers Trigger[] | bind + +---Get the corresponding binding creation function for a trigger +---@param bind bind +---@return string +local function get_binding_function(bind) + if type(bind) == "number" and bind <= 5 and bind > 0 then return "button" - elseif type(trigger) == "string" then + elseif type(bind) == "string" then return "key" end - error("trigger can only be a mouse button or a key", 2) + error("bind can only be a mouse button or a key", 2) end -local function translate_binding(binding, trigger, multiple) - local value = nil - if multiple then - value = trigger[2] - trigger = trigger[1] +--- Translate a qbind binding into an awful binding +---@param binding Binding +---@param trigger Trigger | bind +---@param multi boolean specifies whether this is a multi trigger bind +---@return awful.key +local function translate_binding(binding, trigger, multi) + local awful_binding + if multi then + awful_binding = { + modifiers = binding.mods, + [get_binding_function(trigger[1])] = trigger[1], + on_press = function(...) + binding.press(trigger[2], ...) + end, + } + else + awful_binding = { + modifiers = binding.mods, + [get_binding_function(trigger --[[@as bind]])] = trigger, + on_press = binding.press, + } end - local awful_binding = { - modifiers = binding.mods, - [get_binding_function(trigger)] = trigger, - on_press = multiple and function(...) binding.press(value, ...) end or binding.press - } + awful_binding.description = binding.desc + awful_binding.group = binding.group - if binding.desc then - awful_binding.description = binding.desc - end - if binding.group then - awful_binding.group = binding.group - end - - return awful[get_binding_function(trigger)](awful_binding) + return awful[ + get_binding_function(multi and trigger[1] or trigger --[[@as bind]]) + ](awful_binding) end +--- Create a new binding +---@param binding Binding +---@return awful.key[] function qbind:new(binding) - if not binding.hidden then table.insert(qstore.bindings, binding) end + if not binding.hidden then + table.insert(qstore.bindings, binding) + end binding.mods = binding.mods or {} local awful_bindings = {} if type(binding.triggers) == "table" then - for _, trigger in ipairs(binding.triggers) do + for _, trigger in + ipairs(binding.triggers --[[@as Trigger[]]) + do table.insert(awful_bindings, translate_binding(binding, trigger, true)) end elseif type(binding.triggers) == "string" or type(binding.triggers) == "number" then - return translate_binding(binding, binding.triggers, false) + return translate_binding(binding, binding.triggers --[[@as bind]], false) else - error("binding.triggers can only be a string or a table") + error "binding.triggers can only be a string or a table" end -- for some reason multi-trigger bindings only work if i do this diff --git a/.config/awesome/quarrel/delegate.lua b/.config/awesome/quarrel/delegate.lua new file mode 100644 index 0000000..54db786 --- /dev/null +++ b/.config/awesome/quarrel/delegate.lua @@ -0,0 +1,14 @@ +--- Capture `fn`'s upvalues and pass to `delegate` +---@param delegate fun(env: table<string, any>, _: ...): ... +---@param fn function +---@return fun(...): ... +return function(delegate, fn) + local upvalues = {} + for i = 1, debug.getinfo(fn, "u").nups do + local name, value = debug.getupvalue(fn, i) + upvalues[name] = value + end + return function(...) + return delegate(upvalues, ...) + end +end diff --git a/.config/awesome/quarrel/fs.lua b/.config/awesome/quarrel/fs.lua index 89a1bc6..502f189 100644 --- a/.config/awesome/quarrel/fs.lua +++ b/.config/awesome/quarrel/fs.lua @@ -1,7 +1,12 @@ -local GFile = require "lgi".Gio.File +local GFile = require("lgi").Gio.File +---@class QuarrelFs local qfs = {} +--- Read a file with the specified format (or "a") and close the file +---@param path string +---@param format openmode +---@return any function qfs.read(path, format) local f = assert(io.open(path, "r")) local c = f:read(format or "a") @@ -9,13 +14,23 @@ function qfs.read(path, format) return c end +--- List files in a directory +---@param path string +---@param absolute boolean? +---@return table function qfs.ls_files(path, absolute) local files = GFile.new_for_path(path):enumerate_children("standard::*", 0) local files_filtered = {} - if not files then return {} end + if not files then + return {} + end - for file in function() return files:next_file() end do + for file in + function() + return files:next_file() + end + do if file:get_file_type() == "REGULAR" then local file_name = file:get_display_name() file_name = absolute and (path:gsub("[/]*$", "") .. "/" .. file_name) or file_name diff --git a/.config/awesome/quarrel/init.lua b/.config/awesome/quarrel/init.lua index 025d899..96f7f5b 100644 --- a/.config/awesome/quarrel/init.lua +++ b/.config/awesome/quarrel/init.lua @@ -1,17 +1,22 @@ -local n = require "naughty".notification +local n = require("naughty").notification +---@class Quarrel local quarrel = {} +--- Send a notification with the specified message +---@param message any function quarrel.debug(message) n { message = tostring(message) } end -function quarrel.is_restart() - awesome.register_xproperty("is_restart", "boolean") - local restart_detected = awesome.get_xproperty("is_restart") ~= nil - awesome.set_xproperty("is_restart", true) - - return restart_detected - end +--- Check if there was a restart +---@return boolean +function quarrel.is_restart() + awesome.register_xproperty("is_restart", "boolean") + local restart_detected = awesome.get_xproperty "is_restart" ~= nil + awesome.set_xproperty("is_restart", true) + + return restart_detected +end return quarrel diff --git a/.config/awesome/quarrel/json.lua b/.config/awesome/quarrel/json.lua index 711ef78..5bcc5ff 100644 --- a/.config/awesome/quarrel/json.lua +++ b/.config/awesome/quarrel/json.lua @@ -31,111 +31,103 @@ local json = { _version = "0.1.2" } local encode local escape_char_map = { - [ "\\" ] = "\\", - [ "\"" ] = "\"", - [ "\b" ] = "b", - [ "\f" ] = "f", - [ "\n" ] = "n", - [ "\r" ] = "r", - [ "\t" ] = "t", + ["\\"] = "\\", + ['"'] = '"', + ["\b"] = "b", + ["\f"] = "f", + ["\n"] = "n", + ["\r"] = "r", + ["\t"] = "t", } -local escape_char_map_inv = { [ "/" ] = "/" } +local escape_char_map_inv = { ["/"] = "/" } for k, v in pairs(escape_char_map) do - escape_char_map_inv[v] = k + escape_char_map_inv[v] = k end - local function escape_char(c) - return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) + return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) end - local function encode_nil(val) - return "null" + return "null" end - local function encode_table(val, stack) - local res = {} - stack = stack or {} - - -- Circular reference? - if stack[val] then error("circular reference") end - - stack[val] = true - - if rawget(val, 1) ~= nil or next(val) == nil then - -- Treat as array -- check keys are valid and it is not sparse - local n = 0 - for k in pairs(val) do - if type(k) ~= "number" then - error("invalid table: mixed or invalid key types") - end - n = n + 1 - end - if n ~= #val then - error("invalid table: sparse array") - end - -- Encode - for i, v in ipairs(val) do - table.insert(res, encode(v, stack)) + local res = {} + stack = stack or {} + + -- Circular reference? + if stack[val] then + error "circular reference" end - stack[val] = nil - return "[" .. table.concat(res, ",") .. "]" - - else - -- Treat as an object - for k, v in pairs(val) do - if type(k) ~= "string" then - error("invalid table: mixed or invalid key types") - end - table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) + + stack[val] = true + + if rawget(val, 1) ~= nil or next(val) == nil then + -- Treat as array -- check keys are valid and it is not sparse + local n = 0 + for k in pairs(val) do + if type(k) ~= "number" then + error "invalid table: mixed or invalid key types" + end + n = n + 1 + end + if n ~= #val then + error "invalid table: sparse array" + end + -- Encode + for i, v in ipairs(val) do + table.insert(res, encode(v, stack)) + end + stack[val] = nil + return "[" .. table.concat(res, ",") .. "]" + else + -- Treat as an object + for k, v in pairs(val) do + if type(k) ~= "string" then + error "invalid table: mixed or invalid key types" + end + table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) + end + stack[val] = nil + return "{" .. table.concat(res, ",") .. "}" end - stack[val] = nil - return "{" .. table.concat(res, ",") .. "}" - end end - local function encode_string(val) - return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' + return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' end - local function encode_number(val) - -- Check for NaN, -inf and inf - if val ~= val or val <= -math.huge or val >= math.huge then - error("unexpected number value '" .. tostring(val) .. "'") - end - return string.format("%.14g", val) + -- Check for NaN, -inf and inf + if val ~= val or val <= -math.huge or val >= math.huge then + error("unexpected number value '" .. tostring(val) .. "'") + end + return string.format("%.14g", val) end - local type_func_map = { - [ "nil" ] = encode_nil, - [ "table" ] = encode_table, - [ "string" ] = encode_string, - [ "number" ] = encode_number, - [ "boolean" ] = tostring, + ["nil"] = encode_nil, + ["table"] = encode_table, + ["string"] = encode_string, + ["number"] = encode_number, + ["boolean"] = tostring, } - encode = function(val, stack) - local t = type(val) - local f = type_func_map[t] - if f then - return f(val, stack) - end - error("unexpected type '" .. t .. "'") + local t = type(val) + local f = type_func_map[t] + if f then + return f(val, stack) + end + error("unexpected type '" .. t .. "'") end - function json.encode(val) - return ( encode(val) ) + return (encode(val)) end - ------------------------------------------------------------------------------- -- Decode ------------------------------------------------------------------------------- @@ -143,246 +135,238 @@ end local parse local function create_set(...) - local res = {} - for i = 1, select("#", ...) do - res[ select(i, ...) ] = true - end - return res + local res = {} + for i = 1, select("#", ...) do + res[select(i, ...)] = true + end + return res end -local space_chars = create_set(" ", "\t", "\r", "\n") -local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") -local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") -local literals = create_set("true", "false", "null") +local space_chars = create_set(" ", "\t", "\r", "\n") +local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") +local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") +local literals = create_set("true", "false", "null") local literal_map = { - [ "true" ] = true, - [ "false" ] = false, - [ "null" ] = nil, + ["true"] = true, + ["false"] = false, + ["null"] = nil, } - local function next_char(str, idx, set, negate) - for i = idx, #str do - if set[str:sub(i, i)] ~= negate then - return i + for i = idx, #str do + if set[str:sub(i, i)] ~= negate then + return i + end end - end - return #str + 1 + return #str + 1 end - local function decode_error(str, idx, msg) - local line_count = 1 - local col_count = 1 - for i = 1, idx - 1 do - col_count = col_count + 1 - if str:sub(i, i) == "\n" then - line_count = line_count + 1 - col_count = 1 + local line_count = 1 + local col_count = 1 + for i = 1, idx - 1 do + col_count = col_count + 1 + if str:sub(i, i) == "\n" then + line_count = line_count + 1 + col_count = 1 + end end - end - error( string.format("%s at line %d col %d", msg, line_count, col_count) ) + error(string.format("%s at line %d col %d", msg, line_count, col_count)) end - local function codepoint_to_utf8(n) - -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa - local f = math.floor - if n <= 0x7f then - return string.char(n) - elseif n <= 0x7ff then - return string.char(f(n / 64) + 192, n % 64 + 128) - elseif n <= 0xffff then - return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) - elseif n <= 0x10ffff then - return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, - f(n % 4096 / 64) + 128, n % 64 + 128) - end - error( string.format("invalid unicode codepoint '%x'", n) ) + -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa + local f = math.floor + if n <= 0x7f then + return string.char(n) + elseif n <= 0x7ff then + return string.char(f(n / 64) + 192, n % 64 + 128) + elseif n <= 0xffff then + return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) + elseif n <= 0x10ffff then + return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, f(n % 4096 / 64) + 128, n % 64 + 128) + end + error(string.format("invalid unicode codepoint '%x'", n)) end - local function parse_unicode_escape(s) - local n1 = tonumber( s:sub(1, 4), 16 ) - local n2 = tonumber( s:sub(7, 10), 16 ) - -- Surrogate pair? - if n2 then - return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) - else - return codepoint_to_utf8(n1) - end + local n1 = tonumber(s:sub(1, 4), 16) + local n2 = tonumber(s:sub(7, 10), 16) + -- Surrogate pair? + if n2 then + return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) + else + return codepoint_to_utf8(n1) + end end - local function parse_string(str, i) - local res = "" - local j = i + 1 - local k = j - - while j <= #str do - local x = str:byte(j) - - if x < 32 then - decode_error(str, j, "control character in string") - - elseif x == 92 then -- `\`: Escape - res = res .. str:sub(k, j - 1) - j = j + 1 - local c = str:sub(j, j) - if c == "u" then - local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) - or str:match("^%x%x%x%x", j + 1) - or decode_error(str, j - 1, "invalid unicode escape in string") - res = res .. parse_unicode_escape(hex) - j = j + #hex - else - if not escape_chars[c] then - decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") + local res = "" + local j = i + 1 + local k = j + + while j <= #str do + local x = str:byte(j) + + if x < 32 then + decode_error(str, j, "control character in string") + elseif x == 92 then -- `\`: Escape + res = res .. str:sub(k, j - 1) + j = j + 1 + local c = str:sub(j, j) + if c == "u" then + local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) + or str:match("^%x%x%x%x", j + 1) + or decode_error(str, j - 1, "invalid unicode escape in string") + res = res .. parse_unicode_escape(hex) + j = j + #hex + else + if not escape_chars[c] then + decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") + end + res = res .. escape_char_map_inv[c] + end + k = j + 1 + elseif x == 34 then -- `"`: End of string + res = res .. str:sub(k, j - 1) + return res, j + 1 end - res = res .. escape_char_map_inv[c] - end - k = j + 1 - elseif x == 34 then -- `"`: End of string - res = res .. str:sub(k, j - 1) - return res, j + 1 + j = j + 1 end - j = j + 1 - end - - decode_error(str, i, "expected closing quote for string") + decode_error(str, i, "expected closing quote for string") end - local function parse_number(str, i) - local x = next_char(str, i, delim_chars) - local s = str:sub(i, x - 1) - local n = tonumber(s) - if not n then - decode_error(str, i, "invalid number '" .. s .. "'") - end - return n, x + local x = next_char(str, i, delim_chars) + local s = str:sub(i, x - 1) + local n = tonumber(s) + if not n then + decode_error(str, i, "invalid number '" .. s .. "'") + end + return n, x end - local function parse_literal(str, i) - local x = next_char(str, i, delim_chars) - local word = str:sub(i, x - 1) - if not literals[word] then - decode_error(str, i, "invalid literal '" .. word .. "'") - end - return literal_map[word], x + local x = next_char(str, i, delim_chars) + local word = str:sub(i, x - 1) + if not literals[word] then + decode_error(str, i, "invalid literal '" .. word .. "'") + end + return literal_map[word], x end - local function parse_array(str, i) - local res = {} - local n = 1 - i = i + 1 - while 1 do - local x - i = next_char(str, i, space_chars, true) - -- Empty / end of array? - if str:sub(i, i) == "]" then - i = i + 1 - break - end - -- Read token - x, i = parse(str, i) - res[n] = x - n = n + 1 - -- Next token - i = next_char(str, i, space_chars, true) - local chr = str:sub(i, i) + local res = {} + local n = 1 i = i + 1 - if chr == "]" then break end - if chr ~= "," then decode_error(str, i, "expected ']' or ','") end - end - return res, i + while 1 do + local x + i = next_char(str, i, space_chars, true) + -- Empty / end of array? + if str:sub(i, i) == "]" then + i = i + 1 + break + end + -- Read token + x, i = parse(str, i) + res[n] = x + n = n + 1 + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "]" then + break + end + if chr ~= "," then + decode_error(str, i, "expected ']' or ','") + end + end + return res, i end - local function parse_object(str, i) - local res = {} - i = i + 1 - while 1 do - local key, val - i = next_char(str, i, space_chars, true) - -- Empty / end of object? - if str:sub(i, i) == "}" then - i = i + 1 - break - end - -- Read key - if str:sub(i, i) ~= '"' then - decode_error(str, i, "expected string for key") - end - key, i = parse(str, i) - -- Read ':' delimiter - i = next_char(str, i, space_chars, true) - if str:sub(i, i) ~= ":" then - decode_error(str, i, "expected ':' after key") - end - i = next_char(str, i + 1, space_chars, true) - -- Read value - val, i = parse(str, i) - -- Set - res[key] = val - -- Next token - i = next_char(str, i, space_chars, true) - local chr = str:sub(i, i) + local res = {} i = i + 1 - if chr == "}" then break end - if chr ~= "," then decode_error(str, i, "expected '}' or ','") end - end - return res, i + while 1 do + local key, val + i = next_char(str, i, space_chars, true) + -- Empty / end of object? + if str:sub(i, i) == "}" then + i = i + 1 + break + end + -- Read key + if str:sub(i, i) ~= '"' then + decode_error(str, i, "expected string for key") + end + key, i = parse(str, i) + -- Read ':' delimiter + i = next_char(str, i, space_chars, true) + if str:sub(i, i) ~= ":" then + decode_error(str, i, "expected ':' after key") + end + i = next_char(str, i + 1, space_chars, true) + -- Read value + val, i = parse(str, i) + -- Set + res[key] = val + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "}" then + break + end + if chr ~= "," then + decode_error(str, i, "expected '}' or ','") + end + end + return res, i end - local char_func_map = { - [ '"' ] = parse_string, - [ "0" ] = parse_number, - [ "1" ] = parse_number, - [ "2" ] = parse_number, - [ "3" ] = parse_number, - [ "4" ] = parse_number, - [ "5" ] = parse_number, - [ "6" ] = parse_number, - [ "7" ] = parse_number, - [ "8" ] = parse_number, - [ "9" ] = parse_number, - [ "-" ] = parse_number, - [ "t" ] = parse_literal, - [ "f" ] = parse_literal, - [ "n" ] = parse_literal, - [ "[" ] = parse_array, - [ "{" ] = parse_object, + ['"'] = parse_string, + ["0"] = parse_number, + ["1"] = parse_number, + ["2"] = parse_number, + ["3"] = parse_number, + ["4"] = parse_number, + ["5"] = parse_number, + ["6"] = parse_number, + ["7"] = parse_number, + ["8"] = parse_number, + ["9"] = parse_number, + ["-"] = parse_number, + ["t"] = parse_literal, + ["f"] = parse_literal, + ["n"] = parse_literal, + ["["] = parse_array, + ["{"] = parse_object, } - parse = function(str, idx) - local chr = str:sub(idx, idx) - local f = char_func_map[chr] - if f then - return f(str, idx) - end - decode_error(str, idx, "unexpected character '" .. chr .. "'") + local chr = str:sub(idx, idx) + local f = char_func_map[chr] + if f then + return f(str, idx) + end + decode_error(str, idx, "unexpected character '" .. chr .. "'") end - function json.decode(str) - if type(str) ~= "string" then - error("expected argument of type string, got " .. type(str)) - end - local res, idx = parse(str, next_char(str, 1, space_chars, true)) - idx = next_char(str, idx, space_chars, true) - if idx <= #str then - decode_error(str, idx, "trailing garbage") - end - return res + if type(str) ~= "string" then + error("expected argument of type string, got " .. type(str)) + end + local res, idx = parse(str, next_char(str, 1, space_chars, true)) + idx = next_char(str, idx, space_chars, true) + if idx <= #str then + decode_error(str, idx, "trailing garbage") + end + return res end - return json diff --git a/.config/awesome/quarrel/lua-rust.tar.gz b/.config/awesome/quarrel/lua-rust.tar.gz Binary files differnew file mode 100644 index 0000000..5ff33fd --- /dev/null +++ b/.config/awesome/quarrel/lua-rust.tar.gz diff --git a/.config/awesome/quarrel/markup.lua b/.config/awesome/quarrel/markup.lua new file mode 100644 index 0000000..d206530 --- /dev/null +++ b/.config/awesome/quarrel/markup.lua @@ -0,0 +1,30 @@ +--- Apply markup to a file +---@param content string +---@param args { bold: boolean, italic: boolean, fg: string, bg: string } +---@return string +return function(content, args) + args = args or {} + if args.bold then + content = "<b>" .. content .. "</b>" + end + + if args.italic then + content = "<i>" .. content .. "</i>" + end + + local span_content = "" + + if args.fg or args.bg then + if args.fg then + span_content = "foreground='" .. args.fg .. "'" + end + + if args.bg then + span_content = " background='" .. args.bg .. "'" + end + + content = "<span " .. span_content .. ">" .. content .. "</span>" + end + + return content +end diff --git a/.config/awesome/quarrel/math.lua b/.config/awesome/quarrel/math.lua index 886ce13..b16547b 100644 --- a/.config/awesome/quarrel/math.lua +++ b/.config/awesome/quarrel/math.lua @@ -1,5 +1,10 @@ +local gmath = require "gears.math" + +---@class QuarrelMath local qmath = {} +-- TODO: Finish documenting these functions + function qmath.step_value(value, steps) if value > steps[#steps - 1][1] then return steps[#steps - 1][2] @@ -15,6 +20,10 @@ function qmath.translate_range(value, in_min, in_max, out_min, out_max) return out_min + ((out_max - out_min) / (in_max - in_min)) * (value - in_min) end +function qmath.percentage(value, max) + return gmath.round(value / max * 100) +end + function qmath.clamp(value, min, max) return math.max(math.min(value, max), min) end diff --git a/.config/awesome/quarrel/native/Cargo.toml b/.config/awesome/quarrel/native/Cargo.toml index 9cff8fc..8d56c9f 100644 --- a/.config/awesome/quarrel/native/Cargo.toml +++ b/.config/awesome/quarrel/native/Cargo.toml @@ -17,6 +17,13 @@ nix = "0.26.2" chrono = "0.4.24" itertools = "0.10.5" html-escape = "0.2.13" +mpd = { git = "https://github.com/kstep/rust-mpd", features = [ "serde" ], version = "0.1.0" } +cairo-rs = { git = "https://github.com/gtk-rs/gtk-rs-core.git", version = "0.18.0" } +gdk-pixbuf = { git = "https://github.com/gtk-rs/gtk-rs-core.git", version = "0.18.0" } +symphonia = "0.5.3" +dirs = "5.0.1" +once_cell = "1.18.0" + [lib] crate-type = ["cdylib"] diff --git a/.config/awesome/quarrel/native/init.lua b/.config/awesome/quarrel/native/init.lua index 14c66e5..e5d5aab 100644 --- a/.config/awesome/quarrel/native/init.lua +++ b/.config/awesome/quarrel/native/init.lua @@ -1,6 +1,43 @@ +---@meta + local old_cpath = package.cpath local cfg = require("gears.filesystem").get_configuration_dir() package.cpath = package.cpath .. ";" .. cfg .. "quarrel/native/lib?.so" + +---@class Entry +---@field message string +---@field exec { [1]: string, [2]: boolean }? + +---@alias query fun(input: string): Entry[] + +---@class Lenses +---@field [1] query Calculator lense +---@field [2] query Application lense + +---@alias ReadMode "l" | "n" | string + +---@class FileHandle +---@field read fun(self, mode: ReadMode): string | number +---@field write fun(self, content: string): number +---@field lines fun(self): string[] +---@field rewind fun(self) + +---@class Util +---@field decode_html fun(input: string): string +---@field open_file fun(path: string): FileHandle + +---@class Mpd +---@field init + +---@class Net +---@field get_essid fun(): string + +---@class QuarrelNative +---@field lenses Lenses +---@field util Util +---@field mpd Mpd +---@field net Net local qnative = require "qnative" + package.cpath = old_cpath return qnative diff --git a/.config/awesome/quarrel/native/src/lenses/application.rs b/.config/awesome/quarrel/native/src/lenses/application.rs index 0857802..72aba8d 100644 --- a/.config/awesome/quarrel/native/src/lenses/application.rs +++ b/.config/awesome/quarrel/native/src/lenses/application.rs @@ -73,7 +73,7 @@ pub fn query(lua: &Lua, input: String) -> LuaResult<LuaTable> { return None }; - return parse_entry(&entry, &path).ok(); + parse_entry(&entry, &path).ok() }) .collect(); diff --git a/.config/awesome/quarrel/native/src/lenses/calculator.rs b/.config/awesome/quarrel/native/src/lenses/calculator.rs index c79dd42..07f1ee2 100644 --- a/.config/awesome/quarrel/native/src/lenses/calculator.rs +++ b/.config/awesome/quarrel/native/src/lenses/calculator.rs @@ -10,7 +10,7 @@ use crate::lenses::entry::{ Entry, }; -pub fn query<'a>(lua: &Lua, input: String) -> LuaResult<LuaTable> { +pub fn query(lua: &Lua, input: String) -> LuaResult<LuaTable> { let result = match eval(input.trim(), true, Unit::Celsius, false) { Ok(result) => { format!("{result}") diff --git a/.config/awesome/quarrel/native/src/lib.rs b/.config/awesome/quarrel/native/src/lib.rs index d89b610..472313e 100644 --- a/.config/awesome/quarrel/native/src/lib.rs +++ b/.config/awesome/quarrel/native/src/lib.rs @@ -1,8 +1,9 @@ mod lenses; +mod mpd; mod net; +mod util; use mlua::prelude::*; -use html_escape::decode_html_entities_to_string; #[mlua::lua_module] fn qnative(lua: &Lua) -> LuaResult<LuaTable> { @@ -10,14 +11,21 @@ fn qnative(lua: &Lua) -> LuaResult<LuaTable> { lenses.set("1", lua.create_function(lenses::calculator::query)?)?; lenses.set("2", lua.create_function(lenses::application::query)?)?; + let util = lua.create_table()?; + util.set("decode_html", lua.create_function(util::decode_html)?)?; + util.set("open_file", lua.create_function(util::FileHandle::new)?)?; + + let mpd = lua.create_table()?; + mpd.set("init", lua.create_function(mpd::init)?)?; + + let net = lua.create_table()?; + net.set("get_essid", lua.create_function(net::get_first_essid)?)?; + let exports = lua.create_table()?; exports.set("lenses", lenses)?; - exports.set("get_essid", lua.create_function(net::get_first_essid)?)?; - exports.set("decode_html", lua.create_function(|_: &Lua, string: String| { - let mut output = String::new(); - decode_html_entities_to_string(string, &mut output); - Ok(output) - })?)?; + exports.set("mpd", mpd)?; + exports.set("net", net)?; + exports.set("util", util)?; Ok(exports) } diff --git a/.config/awesome/quarrel/native/src/mpd.rs b/.config/awesome/quarrel/native/src/mpd.rs new file mode 100644 index 0000000..08c9e42 --- /dev/null +++ b/.config/awesome/quarrel/native/src/mpd.rs @@ -0,0 +1,142 @@ +use std::{ + ffi::c_void, + fs::File, + net::TcpStream, + path::PathBuf, + sync::mpsc::channel, +}; + +use dirs::home_dir; +use gdk_pixbuf::{ + ffi::GdkPixbuf, + glib::translate::IntoGlibPtr, + traits::PixbufLoaderExt, + Pixbuf, + PixbufLoader, +}; +use mlua::{ + prelude::*, + LuaSerdeExt, +}; +use mpd::Client; +use once_cell::sync::Lazy; +use symphonia::{ + core::{ + formats::FormatOptions, + io::{ + MediaSourceStream, + MediaSourceStreamOptions, + }, + meta::MetadataOptions, + probe::Hint, + }, + default::get_probe, +}; + +static MPD_MUSIC_PATH: Lazy<PathBuf> = Lazy::new(|| { + [ + home_dir().expect("home directory should be set"), + "Music".into(), + ] + .iter() + .collect() +}); + +static COVER_FORMATS: [&str; 2] = ["png", "jpg"]; + +pub struct Connection(Client<TcpStream>); + +impl LuaUserData for Connection { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_method_mut("status", |lua, this: &mut Connection, (): ()| { + lua.to_value(&this.0.status().map_err(LuaError::external)?) + }); + + methods.add_method_mut("song", |lua, this: &mut Connection, (): ()| { + Ok( + if let Some(song) = this.0.currentsong().map_err(LuaError::external)? { + lua.to_value(&song)? + } else { + LuaNil + }, + ) + }); + + methods.add_method( + "get_cover_pixbuf", + |_, _: &Connection, file: String| { + let song_path = MPD_MUSIC_PATH.join(file); + let mut has_external_cover = false; + let mut cover_path = PathBuf::new(); + + for format in COVER_FORMATS { + let cover = song_path + .parent() + .unwrap() + .to_owned() + .join(format!("cover.{}", format)); + if cover.exists() { + has_external_cover = cover.exists(); + cover_path = cover; + break; + } + } + + let mss = MediaSourceStream::new( + Box::new(File::open(song_path)?), + MediaSourceStreamOptions::default(), + ); + + let mut probed = get_probe() + .format( + &Hint::default(), + mss, + &FormatOptions::default(), + &MetadataOptions::default(), + ) + .map_err(LuaError::external)?; + + let visuals; + + if let Some(metadata) = probed.format.metadata().skip_to_latest() { + visuals = metadata.visuals(); + if visuals.is_empty() && has_external_cover { + let pixbuf = Pixbuf::from_file(cover_path).map_err(LuaError::external)?; + + return Ok(( + Some(LuaLightUserData(unsafe { + <Pixbuf as IntoGlibPtr<*mut GdkPixbuf>>::into_glib_ptr(pixbuf) + .cast::<c_void>() + })), + Some(true), + )); + } + + let loader = PixbufLoader::new(); + loader + .write(visuals.first().unwrap().data.as_ref()) + .map_err(LuaError::external)?; + loader.close().map_err(LuaError::external)?; + + return Ok(( + Some(LuaLightUserData(unsafe { + <Pixbuf as IntoGlibPtr<*mut GdkPixbuf>>::into_glib_ptr( + loader.pixbuf().expect("Pixbuf should be initialized"), + ) + .cast::<c_void>() + })), + Some(false), + )); + } + + Ok((None, None)) + }, + ); + } +} + +pub fn init(_: &Lua, _: ()) -> LuaResult<Connection> { + Ok(Connection( + Client::connect("localhost:6600").map_err(LuaError::external)?, + )) +} diff --git a/.config/awesome/quarrel/native/src/net/mod.rs b/.config/awesome/quarrel/native/src/net/mod.rs index 71eaeea..96c853e 100644 --- a/.config/awesome/quarrel/native/src/net/mod.rs +++ b/.config/awesome/quarrel/native/src/net/mod.rs @@ -39,7 +39,12 @@ use wireless::{ ioctl_read_bad!(ioctl_get_interfaces, SIOCGIFCONF, IfConf); ioctl_read_bad!(ioctl_get_essid, SIOCGIWESSID, IwReq); +#[allow(clippy::unnecessary_wraps)] pub fn get_first_essid(_: &Lua, _: ()) -> LuaResult<String> { + Ok(get_first_essid_error().unwrap_or(String::new())) +} + +fn get_first_essid_error() -> LuaResult<String> { type Buffer = [c_char; 1024]; let mut buffer: Buffer = [0; 1024]; diff --git a/.config/awesome/quarrel/native/src/time.rs b/.config/awesome/quarrel/native/src/time.rs deleted file mode 100644 index 9850822..0000000 --- a/.config/awesome/quarrel/native/src/time.rs +++ /dev/null @@ -1,75 +0,0 @@ -use chrono::prelude::*; -use mlua::{ - prelude::*, - LuaSerdeExt, -}; - -// Taken from https://github.com/chronotope/chrono/issues/69#issuecomment-1510506428 -trait NaiveDateExt { - fn days_in_month(&self) -> u32; - fn days_in_year(&self) -> u32; - fn is_leap_year(&self) -> bool; -} - -impl NaiveDateExt for NaiveDate { - fn days_in_month(&self) -> u32 { - let month = self.month(); - match month { - 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, - 4 | 6 | 9 | 11 => 30, - 2 => { - if self.is_leap_year() { - 29 - } else { - 28 - } - } - _ => panic!("Invalid month: {}", month), - } - } - - fn days_in_year(&self) -> u32 { - if self.is_leap_year() { - 366 - } else { - 365 - } - } - - fn is_leap_year(&self) -> bool { - let year = self.year(); - return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); - } -} - -struct Day { - day: u32, - ce: bool, - weekday: Weekday, -} - -pub fn get_calendar_table(lua: &Lua, (year, month, day): (i32, u32, u32)) -> LuaResult<LuaTable> { - let date = - NaiveDate::from_ymd_opt(year, month, day).ok_or(LuaError::external("invalid date"))?; - let days: Vec<Day> = (1..=date.days_in_month()) - .map(|day| NaiveDate::from_ymd_opt(date.year(), date.month(), day).unwrap()) - .map(|date| { - let (ce, year) = date.year_ce(); - Day { - day: date.day(), - ce, - weekday: date.weekday(), - } - }) - .collect(); - - let calendar: Vec<Vec<Day>> = vec![vec![], vec![], vec![], vec![], vec![], vec![], vec![]]; - - if days[1].weekday != Weekday::Mon { - get_calendar_table(lua) - } - - if days.last().unwrap().weekday != Weekday::Sun {} - - Ok(lua.create_table()?) -} diff --git a/.config/awesome/quarrel/native/src/util.rs b/.config/awesome/quarrel/native/src/util.rs new file mode 100644 index 0000000..85a2574 --- /dev/null +++ b/.config/awesome/quarrel/native/src/util.rs @@ -0,0 +1,101 @@ +use std::{ + cell::RefCell, + fs::File, + fs::OpenOptions, + io::{ + Read, + Seek, + Write + }, + rc::Rc, + str::FromStr, +}; + +use html_escape::decode_html_entities_to_string; +use mlua::prelude::*; +use serde::Serialize; + +pub fn decode_html(_: &Lua, string: String) -> LuaResult<String> { + let mut output = String::new(); + decode_html_entities_to_string(string, &mut output); + Ok(output) +} + +enum ReadMode { + Line, + Number, + All, +} + +impl FromStr for ReadMode { + type Err = (); + + fn from_str(value: &str) -> Result<Self, Self::Err> { + Ok(match value { + "l" => Self::Line, + "n" => Self::Number, + _ => Self::All, + }) + } +} + +#[derive(Serialize)] +#[serde(untagged)] +enum StringOrNumber { + String(String), + Number(u64), +} + +pub struct FileHandle(Rc<RefCell<File>>); + +impl LuaUserData for FileHandle { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_method("read", |lua, this: &FileHandle, mode: String| { + let content = this.read()?; + Ok(lua.to_value(&match ReadMode::from_str(&mode).unwrap() { + ReadMode::Line => StringOrNumber::String( + content + .lines() + .next() + .map_or_else(String::new, |slice| slice.trim().to_owned()), + ), + ReadMode::Number => StringOrNumber::Number( + content.trim().parse::<u64>().map_err(LuaError::external)?, + ), + ReadMode::All => StringOrNumber::String(content), + })) + }); + methods.add_method("write", |_, this: &FileHandle, content: String| { + this.write(content.as_bytes()) + }); + methods.add_method("lines", |_, this: &FileHandle, (): ()| { + Ok(this + .read()? + .lines() + .map(ToOwned::to_owned) + .collect::<Vec<String>>()) + }); + methods.add_method("rewind", |_, this: &FileHandle, (): ()| this.rewind()); + } +} + +impl FileHandle { + pub fn new(_: &Lua, path: String) -> LuaResult<Self> { + Ok(Self(Rc::new(RefCell::new(File::open(path)?)))) + // Ok(Self(Rc::new(RefCell::new(OpenOptions::new().write(true).read(true).open(path)?)))) + } + + fn read(&self) -> LuaResult<String> { + let mut content = String::new(); + self.0.borrow_mut().read_to_string(&mut content)?; + Ok(content) + } + + fn rewind(&self) -> LuaResult<()> { + self.0.borrow_mut().rewind().map_err(LuaError::external) + } + + fn write(&self, buf: &[u8]) -> LuaResult<usize> { + self.0.borrow_mut().write(buf).map_err(LuaError::external) + } +} diff --git a/.config/awesome/quarrel/store.lua b/.config/awesome/quarrel/store.lua index 9f6cff2..9422c21 100644 --- a/.config/awesome/quarrel/store.lua +++ b/.config/awesome/quarrel/store.lua @@ -1,3 +1,4 @@ +---@type table local qstore = {} return qstore diff --git a/.config/awesome/quarrel/table.lua b/.config/awesome/quarrel/table.lua index 13ccbce..2ae15ee 100644 --- a/.config/awesome/quarrel/table.lua +++ b/.config/awesome/quarrel/table.lua @@ -1,17 +1,30 @@ +---@class QuarrelTable local qtable = {} +--- Map a function on each element in the table +---@param t table +---@generic T +---@param f fun(v: T): T +---@return table function qtable.map(t, f) local nt = {} - for k,v in pairs(t) do + for k, v in pairs(t) do nt[k] = f(v) end return nt end +--- Filter a table with a function +---@param t table +---@param f fun(v: any): boolean +---@param dict boolean Whether the supplied table is a dictionary +---@return table function qtable.filter(t, f, dict) local nt = {} - for k,v in pairs(t) do - if not f(v) then goto continue end + for k, v in pairs(t) do + if not f(v) then + goto continue + end if dict then nt[k] = v else @@ -40,9 +53,9 @@ function qtable.onext(t, state) t.__oindex = __gen_oindex(t) key = t.__oindex[1] else - for i = 1,#t.__oindex do + for i = 1, #t.__oindex do if t.__oindex[i] == state then - key = t.__oindex[i+1] + key = t.__oindex[i + 1] end end end diff --git a/.config/awesome/quarrel/ui.lua b/.config/awesome/quarrel/ui.lua index 274d48a..1db4a70 100644 --- a/.config/awesome/quarrel/ui.lua +++ b/.config/awesome/quarrel/ui.lua @@ -2,69 +2,68 @@ local awful = require "awful" local gtable = require "gears.table" local qbind = require "quarrel.bind" local qvars = require "quarrel.vars" +local rtimed = require "lib.rubato.timed" local wibox = require "wibox" +---@class QuarrelUi local qui = {} -function qui.markup_fg(color, text) - return "<span color=\"" .. color .. "\">" .. text .. "</span>" -end - -function qui.markup_bg(color, text) - return "<span bgcolor=\"" .. color .. "\">" .. text .. "</span>" -end - +--- Return qvars.text_font with size scaled by factor +---@param factor number +---@return string +---@see QuarrelVars.text_font function qui.font(factor) return qvars.text_font .. " " .. qvars.font_size * (factor or 1) end +--- Inject background widget styling into target +---@param target table +---@return table function qui.styled(target) return gtable.crush({ bg = qvars.colors.bg, border_color = qvars.colors.bright.black, border_width = qvars.border_width, - shape = qvars.shape + shape = qvars.shape, }, target) end +--- Generate a styled popup +---@param target table +---@return table function qui.popup(target) target.widget = { widget = wibox.container.margin, margins = qvars.big_padding, - target.widget + target.widget, } return awful.popup(qui.styled(target)) end -function qui.tooltip(objects, callback) - awful.tooltip(qui.styled { - objects = objects, - timer_function = callback, - margin_leftright = qvars.padding, - margin_topbottom = qvars.padding - }) -end - +--- Generate svg recolor string +---@param color string +---@return string function qui.recolor(color) return "svg{fill:" .. color .. "}" end -function qui.icon(image, color, target) - local widget = { +--- Generate icon widget +---@param args table +---@return table +function qui.icon(args) + return gtable.crush({ widget = wibox.widget.imagebox, - image = image, + image = args.icon, forced_width = qvars.char_height, forced_height = qvars.char_height, - stylesheet = qui.recolor(color or qvars.colors.fg) - } - if target then - return gtable.crush(widget, target) - else - return widget - end + stylesheet = qui.recolor(args.color or qvars.colors.fg), + }, args.widget or {}) end +--- Generate button widget +---@param args table +---@return table function qui.button(args) args.press = args.press or function(_) end local widget = wibox.widget(gtable.crush({ @@ -73,7 +72,7 @@ function qui.button(args) forced_height = qvars.char_height, forced_width = qvars.char_height, stylesheet = qui.recolor(qvars.colors.fg), - press = args.press + press = args.press, }, args.widget or {})) widget.buttons = { @@ -82,16 +81,19 @@ function qui.button(args) press = function() widget:press() end, - hidden = true - } + hidden = true, + }, } return widget end +--- Generate toggle widget +---@param args table +---@return table function qui.toggle(args) args.press = args.press or function(_) end - local widget = qui.button({ + local widget = qui.button { widget = gtable.crush({ toggled = false, silent_press = function(self, state) @@ -100,7 +102,7 @@ function qui.toggle(args) else self.toggled = not self.toggled end - + if self.toggled then self.image = args.on else @@ -114,10 +116,30 @@ function qui.toggle(args) self:silent_press() end args.press(self) - end - }) + end, + } return widget end +---@param widget wibox.widget.base +---@param cursor string +function qui.hoverable(widget, cursor) + local hovering = false + + widget:connect_signal("mouse::enter", function() + local w = mouse.current_wibox + if w then + w.cursor = cursor + end + end) + + widget:connect_signal("mouse::leave", function() + local w = mouse.current_wibox + if w then + w.cursor = "left_ptr" + end + end) +end + return qui diff --git a/.config/awesome/quarrel/vars.lua b/.config/awesome/quarrel/vars.lua index 27da3f2..1983343 100644 --- a/.config/awesome/quarrel/vars.lua +++ b/.config/awesome/quarrel/vars.lua @@ -1,19 +1,26 @@ -local btns = require "awful".button.names +local awful = require "awful" +local btns = awful.button.names local gears = require "gears" local xresources = require "beautiful.xresources" local x_col = xresources.get_current_theme() local dpi = xresources.apply_dpi local wibox = require "wibox" +---@class QuarrelVars local qvars = {} -qvars.anim_duration = 0.15 +qvars.anim_duration = 0.20 qvars.anim_intro = qvars.anim_duration / 4 -qvars.notif_timeout = 3 + qvars.anim_duration * 2 +qvars.notif_timeout = 3 -function qvars.shape(cr,w,h) - gears.shape.rounded_rect(cr,w,h,dpi(4)) +--- Clip Cairo context +---@param cr cairo_surface Cairo surface +---@param w integer Widget width +---@param h integer Widget height +---@return nil +function qvars.shape(cr, w, h) + gears.shape.rounded_rect(cr, w, h, dpi(4)) end qvars.border_width = dpi(1.5) @@ -21,14 +28,19 @@ qvars.border_width = dpi(1.5) qvars.padding = dpi(4) qvars.big_padding = dpi(8) -qvars.text_font = "Fira Code Nerd Font Mono Medium" -qvars.font_size = 8 +-- qvars.text_font = "Fira Code Nerd Font Mono Medium" +-- qvars.text_font = "Iosevka Comfy SemiBold" +qvars.text_font = "Iosevka Comfy Regular" +-- qvars.font_size = 8 +qvars.font_size = 9 qvars.font = qvars.text_font .. " " .. qvars.font_size -local char_width, char_height = wibox.widget { - widget = wibox.widget.textbox, - text = "a" -}:get_preferred_size_at_dpi(screen[1].dpi) +local char_width, char_height = wibox + .widget({ + widget = wibox.widget.textbox, + text = "a", + }) + :get_preferred_size_at_dpi(awful.screen.focused().dpi) qvars.char_height = char_height qvars.char_width = char_width @@ -62,11 +74,11 @@ qvars.colors = { }, dim = { - fg = "#8893a5", - bg = "#20262e" + fg = "#77828c", + bg = "#161b22", }, - transparent = "#00000000" + transparent = "#00000000", } -- taken from https://github.com/bew/dotfiles/blob/ab9bb1935783f7a31ef777b1d7e26d53f35df864/gui/wezterm/cfg_utils.lua @@ -77,15 +89,16 @@ qvars.mods = setmetatable({ _SHORT_MAP = { C = "Control", S = "Shift", A = "Mod1 resolved_mods[i] = self._SHORT_MAP[key:sub(i, i)] end return resolved_mods - end + end, }) +---@enum buttons qvars.btns = { left = btns.LEFT, right = btns.RIGHT, middle = btns.MIDDLE, up = btns.SCROLL_UP, - down = btns.SCROLL_DOWN + down = btns.SCROLL_DOWN, } return qvars diff --git a/.config/awesome/rc.lua b/.config/awesome/rc.lua index 67a8187..be5b3f1 100644 --- a/.config/awesome/rc.lua +++ b/.config/awesome/rc.lua @@ -17,3 +17,36 @@ require "signals" require "services" require "misc" require "ui" + + +-- require "curious" +-- local gdebug = require "gears.debug" +-- +-- local gobject = require "gears.object" +-- +-- local wrap = gobject { class = { _inner = {}}} +-- +-- local lmonitor = require "lib.lit.monitor" +-- watch = lmonitor.watch_monitor(function(err, event) +-- -- if err then print(err) end +-- -- gdebug.dump(event) +-- end) +-- watch:watch("/home/delta/.config/awesome/test.txt") +-- -- wrap._inner.watch = watch +-- gdebug.dump(watch) + +-- local lit = require "lib.lit" +-- local gfs = require "gears.filesystem" +-- if not gfs.file_readable(gfs.get_configuration_dir() .. "/test_restart.txt") then +-- require "awful".spawn({"touch", gfs.get_configuration_dir() .. "/test_restart.txt"}) +-- awesome.connect_signal("exit", lit.audio.notify_lua_close) +-- local con = lit.audio.new_connection() +-- con:get_sinks(function(err, sinks) +-- if not err then +-- require "gears.debug".dump(sinks, "sinks") +-- else +-- print "lolol error in sinks" +-- end +-- end) +-- -- awesome.restart() +-- end diff --git a/.config/awesome/services/backlight.lua b/.config/awesome/services/backlight.lua new file mode 100644 index 0000000..02ed808 --- /dev/null +++ b/.config/awesome/services/backlight.lua @@ -0,0 +1,34 @@ +local backlight = require("lib.lit").backlight.new("amdgpu_bl1", 5) +local gobject = require "gears.object" +local gtimer = require "gears.timer" +local naughty = require "naughty" +local phosphor = require "assets.phosphor" +local qmath = require "quarrel.math" +local qvars = require "quarrel.vars" + +local backlight_wrapper = gobject { + class = { + set = function(level, handler) + backlight.set(level, handler) + end, + get = function() + return backlight.get() + end, + }, +} + +backlight:connect_signal("brightness", function(_, brightness) + local icon_data = qmath.step_value(brightness, { + { 0, "cloud_moon" }, + { 51, "moon" }, + { 102, "sun_horizon" }, + { 153, "sun_dim" }, + { 204, "sun" }, + { 255 }, + }) + + backlight_wrapper:emit_signal("icon", phosphor[icon_data .. "_fill"], qvars.colors.fg) + backlight_wrapper:emit_signal("value", brightness) +end) + +return backlight_wrapper diff --git a/.config/awesome/services/battery.lua b/.config/awesome/services/battery.lua new file mode 100644 index 0000000..98bacc9 --- /dev/null +++ b/.config/awesome/services/battery.lua @@ -0,0 +1,81 @@ +local UPower = require("lgi").UPowerGlib +local gobject = require "gears.object" +local gtimer = require "gears.timer" +local phosphor = require "assets.phosphor" +local qmath = require "quarrel.math" +local qvars = require "quarrel.vars" + +local upower = assert(UPower.Client.new_full()) +local inner + +for _, device in ipairs(upower:get_devices()) do + if UPower.Device.kind_to_string(device.kind) == "battery" and device.model == "Primary" then + inner = device + break + end +end + +local _level, _state + +---@class Battery: gears.object +---@field _private { level: number, state: string} +local battery = gobject { + class = { + set_level = function(self, level) + _level = level + self:emit_signal("level", level) + end, + get_level = function() + return _level + end, + + set_state = function(self, state) + _state = state + self:emit_signal("state", state) + end, + get_state = function() + return _state + end, + }, + enable_properties = true, +} + +local function icon_handler() + local icon_data = battery.state == "charging" and { "charging", "green" } + or qmath.step_value(battery.level, { + { 0, { "empty", "red" } }, + { 20, { "low", "red" } }, + { 40, { "medium", "yellow" } }, + { 60, { "high", "green" } }, + { 80, { "full", "green" } }, + { 100 }, + }) + + battery:emit_signal("icon", phosphor["battery_vertical_" .. icon_data[1] .. "_fill"], qvars.colors[icon_data[2]]) +end + +local function level_handler() + battery.level = qmath.percentage(inner.energy, inner.energy_full) +end + +local function state_handler() + battery.state = UPower.Device.state_to_string(inner.state) +end + +function inner.on_notify.state() + state_handler() + icon_handler() +end + +function inner.on_notify.energy() + level_handler() + icon_handler() +end + +gtimer.delayed_call(function() + state_handler() + level_handler() + icon_handler() +end) + +return battery diff --git a/.config/awesome/services/common.lua b/.config/awesome/services/common.lua index b23a064..ad1f998 100644 --- a/.config/awesome/services/common.lua +++ b/.config/awesome/services/common.lua @@ -1,52 +1,61 @@ local gfs = require "gears.filesystem" local gtimer = require "gears.timer" local phosphor = require "assets.phosphor" -local qfs = require "quarrel.fs" local qjson = require "quarrel.json" local qmath = require "quarrel.math" local qnative = require "quarrel.native" local qstore = require "quarrel.store" local qvars = require "quarrel.vars" +local UPower = require("lgi").UPowerGlib -local q = require "quarrel" - -local function register(name, service, icon) +--- Register a service +---@param name string +---@param service Service +local function register(name, service) gtimer { - timeout = 0.5, + timeout = 1, call_now = true, autostart = true, callback = function() - local service_result = table.pack(service()) + local service_result = table.pack(service[1]()) awesome.emit_signal("services::" .. name, table.unpack(service_result)) - awesome.emit_signal("services::" .. name .. "::icon", icon(table.unpack(service_result))) - end + awesome.emit_signal("services::" .. name .. "::icon", service[2](table.unpack(service_result))) + end, } end +--- Read a file and automatically close it +---@param file FileHandle +---@param format ReadMode? +---@return string | number local function read(file, format) local content = file:read(format or "a") - file:seek("set") + file:rewind() return content end -- create file in case it's not there yet -if not gfs.file_readable("/tmp/wp_audio_status") then +if not gfs.file_readable "/tmp/wp_audio_status" then assert(io.open("/tmp/wp_audio_status", "w")):write("{}"):close() end -qstore.audio_file = assert(io.open("/tmp/wp_audio_status", "r")) -qstore.battery_capacity_file = assert(io.open("/sys/class/power_supply/BAT0/capacity", "r")) -qstore.battery_status_file = assert(io.open("/sys/class/power_supply/BAT0/status", "r")) -qstore.brightness_file = assert(io.open("/sys/class/backlight/amdgpu_bl0/actual_brightness", "r")) -qstore.wifi_file = assert(io.open("/proc/net/wireless", "r")) + +qstore.audio_file = qnative.util.open_file "/tmp/wp_audio_status" +qstore.brightness_file = qnative.util.open_file "/sys/class/backlight/amdgpu_bl1/actual_brightness" +qstore.wifi_file = qnative.util.open_file "/proc/net/wireless" -- follows the format `service = { provider, icon_provider }` +---@class Service +---@field [1] fun(): ... +---@field [2] fun(...): path: string, color: string + +---@type table<string, Service> local services = { audio = { -- volume, muted function() local audio_status = qjson.decode(read(qstore.audio_file)) local default_sink = audio_status["G435 Wireless Gaming Headset Analog Stereo"] - + if not default_sink then return nil, false end @@ -63,29 +72,11 @@ local services = { { 25, "none" }, { 50, "low" }, { 75, "high" }, - { 100 } + { 100 }, }) return phosphor["speaker_simple_" .. icon_data .. "_fill"], qvars.colors.fg - end - }, - battery = { - -- capacity, status - function() - return read(qstore.battery_capacity_file, "n"), read(qstore.battery_status_file, "l") end, - function(capacity, status) - local icon_data = status == "Charging" and { "charging", "green" } or qmath.step_value(capacity, { - { 0, { "empty", "red" } }, - { 20, { "low", "red" } }, - { 40, { "medium", "yellow" } }, - { 60, { "high", "green" } }, - { 80, { "full", "green" } }, - { 100 } - }) - - return phosphor["battery_vertical_" .. icon_data[1] .. "_fill"], qvars.colors[icon_data[2]] - end }, brightness = { -- brightness @@ -99,29 +90,25 @@ local services = { { 102, "sun_horizon" }, { 153, "sun_dim" }, { 204, "sun" }, - { 255 } + { 255 }, }) return phosphor[icon_data .. "_fill"], qvars.colors.fg - end + end, }, wifi = { -- essid, strength, connected function() - local lines = {} - - for line in qstore.wifi_file:lines() do - table.insert(lines, line) - end - qstore.wifi_file:seek("set") + local lines = qstore.wifi_file:lines() + qstore.wifi_file:rewind() - if not lines[3] then + if not lines[3] then return nil, 0, false end - local strength = lines[3]:match("^%s*(.*)"):match("^.-%s+.-%s+.-(%S+)%.") + local strength = lines[3]:match("^%s*(.*)"):match "^.-%s+.-%s+.-(%S+)%." - return qnative.get_essid(), math.floor(tonumber(strength) * 10 / 7), true + return qnative.net.get_essid(), math.floor(tonumber(strength) * 10 / 7), true end, function(_, strength, connected) if not connected then @@ -133,14 +120,14 @@ local services = { { 25, { "low", "yellow" } }, { 50, { "medium", "yellow" } }, { 75, { "high", "green" } }, - { 100 } + { 100 }, }) return phosphor["wifi_" .. icon_data[1] .. "_fill"], qvars.colors[icon_data[2]] - end - } + end, + }, } for name, service in pairs(services) do - register(name, service[1], service[2]) + register(name, service) end diff --git a/.config/awesome/services/init.lua b/.config/awesome/services/init.lua index 4ac624f..dc081ff 100644 --- a/.config/awesome/services/init.lua +++ b/.config/awesome/services/init.lua @@ -1,2 +1,3 @@ require "services.common" require "services.playerctl" +require "services.battery" diff --git a/.config/awesome/services/osd.lua b/.config/awesome/services/osd.lua new file mode 100644 index 0000000..c4fa3e7 --- /dev/null +++ b/.config/awesome/services/osd.lua @@ -0,0 +1,7 @@ +local battery = require "services.battery" +local osd = require "ui.osd" +local phosphor = require "assets.phosphor" + +-- battery:connect_signal("icon", function (self, icon, _) +-- osd.notify(icon, self.level, 100) +-- end) diff --git a/.config/awesome/services/playerctl.lua b/.config/awesome/services/playerctl.lua index 3fb0a3d..f6ee71a 100644 --- a/.config/awesome/services/playerctl.lua +++ b/.config/awesome/services/playerctl.lua @@ -1,5 +1,5 @@ -local playerctl = require "lib.bling.signal.playerctl".lib { - player = { "spotify", "%any" } +local playerctl = require("lib.bling.signal.playerctl").lib { + player = { "spotify", "%any" }, } playerctl:connect_signal("metadata", function(_, ...) @@ -39,7 +39,7 @@ playerctl:connect_signal("exit", function(_, ...) end) playerctl:connect_signal("no_players", function() - awesome.emit_signal("services::playerctl::no_players") + awesome.emit_signal "services::playerctl::no_players" end) return playerctl diff --git a/.config/awesome/signals/awesome.lua b/.config/awesome/signals/awesome.lua index 37b9bdb..4ed3d32 100644 --- a/.config/awesome/signals/awesome.lua +++ b/.config/awesome/signals/awesome.lua @@ -1,27 +1,31 @@ -- Taken from https://www.reddit.com/r/awesomewm/comments/syjolb/comment/hy0xy35/ -awesome.connect_signal('exit', function(reason_restart) - if not reason_restart then return end +awesome.connect_signal("exit", function(reason_restart) + if not reason_restart then + return + end - local file = io.open('/tmp/awesomewm-last-selected-tags', 'w+') + local file = io.open("/tmp/awesomewm-last-selected-tags", "w+") for s in screen do - file:write(s.selected_tag.index, '\n') + file:write(s.selected_tag.index, "\n") end file:close() end) -awesome.connect_signal('startup', function() - local file = io.open('/tmp/awesomewm-last-selected-tags', 'r') - if not file then return end +awesome.connect_signal("startup", function() + local file = io.open("/tmp/awesomewm-last-selected-tags", "r") + if not file then + return + end local selected_tags = {} for line in file:lines() do table.insert(selected_tags, tonumber(line)) end - + for s in screen do local i = selected_tags[s.index] local t = s.tags[i] diff --git a/.config/awesome/signals/client.lua b/.config/awesome/signals/client.lua index 31f3d9c..8788735 100644 --- a/.config/awesome/signals/client.lua +++ b/.config/awesome/signals/client.lua @@ -1,23 +1,15 @@ local qvars = require "quarrel.vars" -local rectangle = require "gears.shape".rectangle +local rectangle = require("gears.shape").rectangle +local conductor = require "ui.conductor" --- io.popen spams logs, don't use --- client.connect_signal("property::name", function(c) --- if not c.pid then return end --- local out = io.popen("readlink /proc/" .. c.pid .. "/exe") --- local name = c.name --- if out ~= nil then --- name = out:read("*a"):sub(0, -2):match("[^\\/]+$") or name --- end --- c.name = string.lower(name) --- end) - -client.connect_signal("request::manage", function (c) +client.connect_signal("request::manage", function(c) c.shape = qvars.shape if c.maximized then c.maximized = false c.maximized = true end + + -- conductor.update() end) local function handle_corners(c) diff --git a/.config/awesome/signals/init.lua b/.config/awesome/signals/init.lua index 5641a96..a18290e 100644 --- a/.config/awesome/signals/init.lua +++ b/.config/awesome/signals/init.lua @@ -2,4 +2,3 @@ require "signals.naughty" require "signals.awesome" require "signals.screen" require "signals.client" - diff --git a/.config/awesome/signals/naughty.lua b/.config/awesome/signals/naughty.lua index 90594c1..cc096e7 100644 --- a/.config/awesome/signals/naughty.lua +++ b/.config/awesome/signals/naughty.lua @@ -1,54 +1,254 @@ -local awful = require "awful" -local beautiful = require "beautiful" +local gdebug = require "gears.debug" local naughty = require "naughty" -local qvars = require "quarrel.vars" -local wibox = require "wibox" - -naughty.connect_signal("request::display", function(n) - naughty.layout.box { - notification = n, - placement = function(d) - return awful.placement.right(d, { - margins = beautiful.useless_gap * 2 - }) - end, - widget_template = { - widget = wibox.container.margin, - margins = qvars.big_padding, - { - { - widget = naughty.widget.icon, - notification = n - }, - { - widget = wibox.container.place, - valign = "center", - halign = "center", - { - { - widget = naughty.widget.title, - notification = n - }, - { - widget = naughty.widget.message, - notification = n - }, - -- spacing = 4, - layout = wibox.layout.fixed.vertical, - } - }, - fill_space = true, - spacing = n.icon and qvars.big_padding or 0, - layout = wibox.layout.fixed.horizontal - } - } +local ndbus = require "naughty.dbus" +local qdelegate = require "quarrel.delegate" +local wicked = require "ui.wicked" + +---@diagnostic disable-next-line:redundant-parameter +ndbus._notif_methods.Notify = qdelegate(function(env, sender, object_path, interface, method, parameters, invocation) + local appname, replaces_id, app_icon, title, text, actions, hints, expire = env.unpack(parameters.value) + + local args = {} + if text ~= "" then + args.message = text + if title ~= "" then + args.title = title + end + else + if title ~= "" then + args.message = title + else + -- FIXME: We have to reply *something* to the DBus invocation. + -- Right now this leads to a memory leak, I think. + return + end + end + + if appname ~= "" then + args.appname = appname --TODO v6 Remove this. + args.app_name = appname + end + + local preset = args.preset or env.cst.config.defaults + local notification + if actions then + args.actions = {} + + for i = 1, #actions, 2 do + local action_id = actions[i] + local action_text = actions[i + 1] + + if action_id == "default" then + args.run = function() + env.sendActionInvoked(notification.id, "default") + notification:destroy(env.cst.notification_closed_reason.dismissed_by_user) + end + elseif action_id ~= nil and action_text ~= nil then + local a = env.naction { + name = action_text, + id = action_id, + position = (i - 1) / 2 + 1, + } + + -- Right now `gears` doesn't have a great icon implementation + -- and `naughty` doesn't depend on `menubar`, so delegate the + -- icon "somewhere" using a request. + if hints["action-icons"] and action_id ~= "" then + naughty.emit_signal("request::action_icon", a, "dbus", { id = action_id }) + end + + a:connect_signal("invoked", function() + env.sendActionInvoked(notification.id, action_id) + + if not notification.resident then + notification:destroy(env.cst.notification_closed_reason.dismissed_by_user) + end + end) + + table.insert(args.actions, a) + end + end + end + args.destroy = function(reason) + env.sendNotificationClosed(notification.id, reason) + end + local legacy_data = { -- This data used to be generated by AwesomeWM's C code + type = "method_call", + interface = interface, + path = object_path, + member = method, + sender = sender, + bus = "session", } -end) + if + not preset.callback + or ( + type(preset.callback) == "function" + and preset.callback(legacy_data, appname, replaces_id, app_icon, title, text, actions, hints, expire) + ) + then + if app_icon ~= "" then + args.app_icon = app_icon + end + + if hints.icon_data or hints.image_data or hints["image-data"] then + -- Icon data is a bit complex and hence needs special care: + -- .value breaks with the array of bytes (ay) that we get here. + -- So, bypass it and look up the needed value differently + local icon_condidates = {} + for k, v in parameters:get_child_value(7 - 1):pairs() do + if k == "image-data" then + icon_condidates[1] = v -- not deprecated + break + elseif k == "image_data" then -- deprecated + icon_condidates[2] = v + elseif k == "icon_data" then -- deprecated + icon_condidates[3] = v + end + end + + -- The order is mandated by the spec. + local icon_data = icon_condidates[1] or icon_condidates[2] or icon_condidates[3] + + -- icon_data is an array: + -- 1 -> width + -- 2 -> height + -- 3 -> rowstride + -- 4 -> has alpha + -- 5 -> bits per sample + -- 6 -> channels + -- 7 -> data + + -- Get the value as a GVariant and then use LGI's special + -- GVariant.data to get that as an LGI byte buffer. That one can + -- then by converted to a string via its __tostring metamethod. + local data = tostring(icon_data:get_child_value(7 - 1).data) + args.image = env.convert_icon(icon_data[1], icon_data[2], icon_data[3], icon_data[6], data) + + -- Convert all animation frames. + if naughty.image_animations_enabled then + args.images = { args.image } + + if #icon_data > 7 then + for frame = 8, #icon_data do + data = tostring(icon_data:get_child_value(frame - 1).data) + + table.insert( + args.images, + env.convert_icon(icon_data[1], icon_data[2], icon_data[3], icon_data[6], data) + ) + end + end + end + end + + -- Alternate ways to set the icon. The specs recommends to allow both + -- the icon and image to co-exist since they serve different purpose. + -- However in case the icon isn't specified, use the image. + args.image = args.image + or hints["image-path"] -- not deprecated + or hints["image_path"] -- deprecated + + if naughty.image_animations_enabled then + args.images = args.images or {} + end + + if replaces_id and replaces_id ~= "" and replaces_id ~= 0 then + args.replaces_id = replaces_id + end + if expire and expire > -1 then + args.timeout = expire / 1000 + end + + args.freedesktop_hints = hints + + -- Not very pretty, but given the current format is documented in the + -- public API... well, whatever... + if hints and hints.urgency then + for name, key in pairs(env.urgency) do + local b = string.char(hints.urgency) + if key == b then + args.urgency = name + end + end + end + + args.urgency = args.urgency or "normal" + + -- Try to update existing objects when possible + notification = naughty.get_by_id(replaces_id) + + if notification then + if not notification._private._unique_sender then + -- If this happens, the notification is either trying to + -- highjack content created within AwesomeWM or it is garbage + -- to begin with. + gdebug.print_warning( + "A notification has been received, but tried to update " + .. "the content of a notification it does not own." + ) + elseif notification._private._unique_sender ~= sender then + -- Nothing says you cannot and some scripts may do it + -- accidentally, but this is rather unexpected. + gdebug.print_warning( + "Notification " + .. notification.title + .. " is being updated" + .. "by a different DBus connection (" + .. sender + .. "), this is " + .. "suspicious. The original connection was " + .. notification._private._unique_sender + ) + end + + for k, v in pairs(args) do + if k == "destroy" then + k = "destroy_cb" + end + notification[k] = v + end + + -- Update the icon if necessary. + if app_icon ~= notification._private.app_icon then + notification._private.app_icon = app_icon + + naughty._emit_signal_if("request::icon", function() + if notification._private.icon then + return true + end + end, notification, "dbus_clear", {}) + end + + -- Even if no property changed, restart the timeout. + notification:reset_timeout() + else + -- Only set the sender for new notifications. + args._unique_sender = sender + args._foreign = true + + notification = env.nnotif(args) + + notification:connect_signal("destroyed", function(_, r) + args.destroy(r) + end) + end + + invocation:return_value(env.GLib.Variant("(u)", { notification.id })) + return + end + + invocation:return_value(env.GLib.Variant("(u)", { env.nnotif._gen_next_id() })) +end, ndbus._notif_methods.Notify) + +naughty.connect_signal("request::display", wicked) naughty.connect_signal("request::display_error", function(message, startup) naughty.notification { urgency = "critical", - title = "Oops, an error happened"..(startup and " during startup!" or "!"), - message = message + title = "Oops, an error happened" .. (startup and " during startup!" or "!"), + message = message, } + + gdebug.print_error("[Error" .. (startup and "during startup" or "") .. "]: " .. message) end) diff --git a/.config/awesome/signals/screen.lua b/.config/awesome/signals/screen.lua index 706bf56..57f4f98 100644 --- a/.config/awesome/signals/screen.lua +++ b/.config/awesome/signals/screen.lua @@ -4,8 +4,11 @@ local gtable = require "gears.table" screen.connect_signal("request::desktop_decoration", function(s) for i, tag in ipairs(cfg.tags) do - awful.tag.add(tostring(i), gtable.crush({ - screen = s - }, tag)) + awful.tag.add( + tostring(i), + gtable.crush({ + screen = s, + }, tag) + ) end end) diff --git a/.config/awesome/stylua.toml b/.config/awesome/stylua.toml new file mode 100644 index 0000000..5cc0b69 --- /dev/null +++ b/.config/awesome/stylua.toml @@ -0,0 +1,5 @@ +call_parentheses = "None" +indent_type = "Spaces" + +[sort_requires] +enabled = true diff --git a/.config/awesome/ui/conductor/init.lua b/.config/awesome/ui/conductor/init.lua index b5ac82b..e6fe552 100644 --- a/.config/awesome/ui/conductor/init.lua +++ b/.config/awesome/ui/conductor/init.lua @@ -3,24 +3,6 @@ local q = require "quarrel" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" -awful.keygrabber { - keybindings = { - awful.key { - modifiers = qvars.mods.A, - key = "Tab", - on_press = awful.client.focus.history.previous - }, - awful.key { - modifiers = qvars.mods.AS, - key = "Tab", - on_press = awful.client.focus.history.next - }, - }, - -- Note that it is using the key name and not the modifier name. - stop_key = "Mod1", - stop_event = "release", - -- start_callback = awful.client.focus.history.disable_tracking, - stop_callback = awful.client.focus.history.enable_tracking, - export_keybindings = true, - autostart = true -} +local conductor = {} + +return conductor diff --git a/.config/awesome/ui/decorations/mpd.lua b/.config/awesome/ui/decorations/mpd.lua new file mode 100644 index 0000000..a03b579 --- /dev/null +++ b/.config/awesome/ui/decorations/mpd.lua @@ -0,0 +1,13 @@ +local mpd = {} + +function mpd.left() end + +function mpd.bottom() end + +function mpd.setup(c) end + +function mpd:next() end + +function mpd:previous() end + +function mpd:stop() end diff --git a/.config/awesome/ui/decorations/wallpaper.lua b/.config/awesome/ui/decorations/wallpaper.lua index dbe54ee..3a204c2 100644 --- a/.config/awesome/ui/decorations/wallpaper.lua +++ b/.config/awesome/ui/decorations/wallpaper.lua @@ -1,4 +1,4 @@ --- local gfs = require "gears.filesystem" +local qmarkup = require "quarrel.markup" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" local wallpaper = require "awful.wallpaper" @@ -16,24 +16,14 @@ screen.connect_signal("request::wallpaper", function(s) widget = wibox.widget.textbox, font = qui.font(1.5), markup = table.concat({ - qui.markup_fg(qvars.colors.red, " ___"), - qui.markup_fg(qvars.colors.green, " /\\ \\"), - qui.markup_fg(qvars.colors.yellow, " /::\\ \\"), - qui.markup_fg(qvars.colors.blue, "/::\\:\\__\\"), - qui.markup_fg(qvars.colors.pink, "\\/\\::/ /"), - qui.markup_fg(qvars.colors.cyan, " \\/__/") - }, "\n") - } - -- { - -- -- image = gfs.get_configuration_dir() .. "assets/nightmare_fuel.png", - -- image = "/home/delta/Desktop/offline vs online.jpg", - -- resize = true, - -- widget = wibox.widget.imagebox, - -- }, - -- valign = "center", - -- halign = "center", - -- tiled = false, - -- widget = wibox.container.tile, - } + qmarkup(" ___", { fg = qvars.colors.red }), + qmarkup(" /\\ \\", { fg = qvars.colors.green }), + qmarkup(" /::\\ \\", { fg = qvars.colors.yellow }), + qmarkup("/::\\:\\__\\", { fg = qvars.colors.blue }), + qmarkup("\\/\\::/ /", { fg = qvars.colors.pink }), + qmarkup(" \\/__/", { fg = qvars.colors.cyan }), + }, "\n"), + }, + }, } end) diff --git a/.config/awesome/ui/fresnel/init.lua b/.config/awesome/ui/fresnel/init.lua index f31da12..9905741 100644 --- a/.config/awesome/ui/fresnel/init.lua +++ b/.config/awesome/ui/fresnel/init.lua @@ -49,10 +49,10 @@ function fresnel:_update(query, scrolled) self._entries_exec = {} layout:reset() - + for _, provider in qtable.opairs(qnative.lenses) do local entries = provider(query) - + table.sort(entries, function(a, b) return a.message < b.message end) @@ -63,72 +63,76 @@ function fresnel:_update(query, scrolled) self._entries_count = #all_providers for i, entry in ipairs(all_providers) do - if i <= self._entries_offset then - goto continue - end - if entries_count == max_entries then break end - table.insert(self._entries_exec, entry.exec) + if i > self._entries_offset then + if entries_count == max_entries then + break + end + table.insert(self._entries_exec, entry.exec) - local entry_widget = wibox.widget { - widget = wibox.container.background, - shape = qvars.shape, - { - widget = wibox.container.margin, - margins = qvars.padding, + local entry_widget = wibox.widget { + widget = wibox.container.background, + shape = qvars.shape, { - widget = wibox.container.constraint, - strategy = "max", - height = qvars.char_height, + widget = wibox.container.margin, + margins = qvars.padding, { + widget = wibox.container.constraint, + strategy = "max", + height = qvars.char_height, { - widget = wibox.container.background, - fg = qvars.colors.dim.fg, + { + widget = wibox.container.background, + fg = qvars.colors.dim.fg, + { + widget = wibox.widget.textbox, + text = entry.provider .. " | ", + }, + }, { widget = wibox.widget.textbox, - text = entry.provider .. " | " - } + text = entry.message, + }, + spacing = qvars.padding, + layout = wibox.layout.fixed.horizontal, }, - { - widget = wibox.widget.textbox, - text = entry.message - }, - spacing = qvars.padding, - layout = wibox.layout.fixed.horizontal - } - } - }, - buttons = { - awful.button { - modifiers = {}, - button = btn.LEFT, - on_press = function() - self:_exec_entry(i) - end - } - }, - _selected = false - } + }, + }, + buttons = { + awful.button { + modifiers = {}, + button = btn.LEFT, + on_press = function() + self:_exec_entry(i) + end, + }, + }, + _selected = false, + } - if self._selected_index + self._entries_offset == i then - entry_widget._selected = true - entry_widget.bg = qvars.colors.black - end + if self._selected_index + self._entries_offset == i then + entry_widget._selected = true + entry_widget.bg = qvars.colors.black + end - entry_widget:connect_signal("mouse::enter", function() - if entry_widget._selected == true then return end - entry_widget.bg = qvars.colors.black - end) + entry_widget:connect_signal("mouse::enter", function() + if entry_widget._selected == true then + return + end + entry_widget.bg = qvars.colors.black + end) - entry_widget:connect_signal("mouse::leave", function() - if entry_widget._selected == true then return end - entry_widget.bg = qvars.colors.bg - end) + entry_widget:connect_signal("mouse::leave", function() + if entry_widget._selected == true then + return + end + entry_widget.bg = qvars.colors.bg + end) - layout:add(entry_widget) + layout:add(entry_widget) - entries_count = entries_count + 1 - self._visible_entries = entries_count - ::continue:: + entries_count = entries_count + 1 + self._visible_entries = entries_count + end end status.text = self._entries_offset + self._selected_index .. "/" .. self._entries_count @@ -141,8 +145,12 @@ fresnel._prompt = al_prompt { ul_cursor = "low", bg_cursor = qvars.colors.black, changed_callback = function(text) - if fresnel._text == text then return end - if fresnel._toggled == false then return end + if fresnel._text == text then + return + end + if fresnel._toggled == false then + return + end fresnel:_update(text) fresnel._text = text end, @@ -154,7 +162,7 @@ fresnel._prompt = al_prompt { fresnel:hide() elseif key == "Up" then local next_index = fresnel._selected_index - 1 - if next_index < 1 and fresnel._entries_offset == 0 then + if next_index < 1 and fresnel._entries_offset == 0 then return elseif next_index < 1 and fresnel._entries_offset > 0 then fresnel._entries_offset = fresnel._entries_offset - 1 @@ -165,9 +173,15 @@ fresnel._prompt = al_prompt { fresnel:_update(fresnel._text, true) elseif key == "Down" then local next_index = fresnel._selected_index + 1 - if next_index > fresnel._visible_entries and fresnel._entries_offset + fresnel._visible_entries == fresnel._entries_count then + if + next_index > fresnel._visible_entries + and fresnel._entries_offset + fresnel._visible_entries == fresnel._entries_count + then return - elseif next_index > fresnel._visible_entries and fresnel._entries_offset + fresnel._visible_entries < fresnel._entries_count then + elseif + next_index > fresnel._visible_entries + and fresnel._entries_offset + fresnel._visible_entries < fresnel._entries_count + then fresnel._entries_offset = fresnel._entries_offset + 1 else fresnel._selected_index = next_index @@ -175,7 +189,7 @@ fresnel._prompt = al_prompt { fresnel:_update(fresnel._text, true) end - end + end, } fresnel._widget = qui.popup { @@ -184,12 +198,12 @@ fresnel._widget = qui.popup { placement = function(d) awful.placement.top(d, { margins = { - top = beautiful.useless_gap * 2 - } + top = beautiful.useless_gap * 2, + }, }) end, - minimum_width = screen[1].geometry.width / 2, - maximum_width = screen[1].geometry.width / 2, + minimum_width = awful.screen.focused().geometry.width / 2, + maximum_width = awful.screen.focused().geometry.width / 2, widget = { { widget = wibox.container.background, @@ -202,13 +216,13 @@ fresnel._widget = qui.popup { { { widget = wibox.widget.textbox, - text = ">" + text = ">", }, { widget = wibox.container.margin, margins = { left = qvars.padding, - right = qvars.padding + right = qvars.padding, }, { widget = wibox.container.constraint, @@ -217,34 +231,37 @@ fresnel._widget = qui.popup { { widget = wibox.container.background, fg = qvars.colors.fg, - fresnel._prompt.textbox - } - } + fresnel._prompt.textbox, + }, + }, }, { widget = wibox.widget.textbox, text = "0/0", - id = "status" + id = "status", }, - layout = wibox.layout.align.horizontal - } - } + layout = wibox.layout.align.horizontal, + }, + }, }, { widget = wibox.container.margin, margins = { - top = qvars.padding + top = qvars.padding, }, { spacing = qvars.padding, layout = wibox.layout.fixed.vertical, - id = "entry_layout" - } + id = "entry_layout", + }, }, - layout = wibox.layout.align.vertical - } + layout = wibox.layout.align.vertical, + }, } -fresnel._widget.maximum_height = qvars.big_padding * 2 + (qvars.padding * 2 + qvars.char_height) * (1 + 10) + qvars.padding + qvars.padding * 9 +fresnel._widget.maximum_height = qvars.big_padding * 2 + + (qvars.padding * 2 + qvars.char_height) * (1 + 10) + + qvars.padding + + qvars.padding * 9 function fresnel:show() self._toggled = true @@ -271,7 +288,7 @@ fresnel._timed = rubato.timed { else fresnel._widget.visible = true end - end + end, } return fresnel diff --git a/.config/awesome/ui/init.lua b/.config/awesome/ui/init.lua index e41644f..dd93ad1 100644 --- a/.config/awesome/ui/init.lua +++ b/.config/awesome/ui/init.lua @@ -1,2 +1,4 @@ require "ui.statusbar" require "ui.decorations" +require "ui.conductor" +require "ui.osd" diff --git a/.config/awesome/ui/insightful/init.lua b/.config/awesome/ui/insightful/init.lua index e8a4a4d..3dcbbea 100644 --- a/.config/awesome/ui/insightful/init.lua +++ b/.config/awesome/ui/insightful/init.lua @@ -64,7 +64,7 @@ insightful._keymap = { XF86AudioStop = "⏹", [tostring(btn.LEFT)] = mouse .. "Left", [tostring(btn.MIDDLE)] = mouse .. "Middle", - [tostring(btn.RIGHT)] = mouse .. "Right" + [tostring(btn.RIGHT)] = mouse .. "Right", } insightful._widget = qui.popup { @@ -73,17 +73,17 @@ insightful._widget = qui.popup { placement = function(d) awful.placement.top(d, { margins = { - top = beautiful.useless_gap * 2 - } + top = beautiful.useless_gap * 2, + }, }) end, - minimum_height = screen[1].geometry.height / 2, - minimum_width = screen[1].geometry.width / 2, + minimum_height = awful.screen.focused().geometry.height / 2, + minimum_width = awful.screen.focused().geometry.width / 2, widget = { layout = wibox.layout.fixed.vertical, spacing = qvars.big_padding, - id = "layout_container" - } + id = "layout_container", + }, } function insightful:_generate() @@ -98,55 +98,59 @@ function insightful:_generate() grouped_binds[group] = {} end - table.insert(grouped_binds[group], { + table.insert(grouped_binds[group], { mods = keybind.mods, triggers = keybind.triggers, - desc = keybind.desc + desc = keybind.desc, }) end for group, keybinds in qtable.opairs(grouped_binds) do local group_layout = { spacing = qvars.padding, - layout = wibox.layout.fixed.vertical + layout = wibox.layout.fixed.vertical, } for _, keybind in ipairs(keybinds) do local key_layout = { - layout = wibox.layout.fixed.horizontal + layout = wibox.layout.fixed.horizontal, } local key_and_desc_layout = { nil, -- key nil, nil, -- description? - layout = wibox.layout.align.horizontal + layout = wibox.layout.align.horizontal, } - + for _, mod in ipairs(keybind.mods) do - table.insert(key_layout, 1, qui.styled { - widget = wibox.container.background, - bg = qvars.colors.bright.black, - border_width = 0, - { - widget = wibox.container.margin, - margins = qvars.padding, + table.insert( + key_layout, + 1, + qui.styled { + widget = wibox.container.background, + bg = qvars.colors.bright.black, + border_width = 0, { - widget = wibox.widget.textbox, - text = insightful._keymap[mod] or mod - } + widget = wibox.container.margin, + margins = qvars.padding, + { + widget = wibox.widget.textbox, + text = insightful._keymap[mod] or mod, + }, + }, } - }) + ) table.insert(key_layout, 2, { widget = wibox.widget.textbox, - text = " + " + text = " + ", }) end if type(keybind.triggers) == "string" or type(keybind.triggers) == "number" then table.insert(key_layout, { widget = wibox.widget.textbox, - text = insightful._keymap[tostring(keybind.triggers)] or keybind.triggers + text = insightful._keymap[tostring(keybind.triggers)] or keybind.triggers, }) elseif type(keybind.triggers) == "table" then local display_trigger = {} @@ -155,7 +159,7 @@ function insightful:_generate() end table.insert(key_layout, { widget = wibox.widget.textbox, - text = table.concat(display_trigger, "/") + text = table.concat(display_trigger, "/"), }) end @@ -165,16 +169,16 @@ function insightful:_generate() fg = qvars.colors.dim.fg, { widget = wibox.widget.textbox, - text = keybind.desc - } + text = keybind.desc, + }, } end key_and_desc_layout[1] = key_layout table.insert(group_layout, key_and_desc_layout) - end - + end + layout_container:add { { { @@ -187,16 +191,16 @@ function insightful:_generate() margins = qvars.padding, { widget = wibox.widget.textbox, - text = group - } - } + text = group, + }, + }, }, nil, - layout = wibox.layout.align.horizontal + layout = wibox.layout.align.horizontal, }, group_layout, spacing = qvars.padding, - layout = wibox.layout.fixed.vertical + layout = wibox.layout.fixed.vertical, } end end @@ -213,11 +217,11 @@ local timed = rubato.timed { else insightful._widget.visible = true end - end + end, } function insightful:toggle() - if first_time then + if first_time then insightful:_generate() first_time = false end diff --git a/.config/awesome/ui/osd.lua b/.config/awesome/ui/osd.lua new file mode 100644 index 0000000..fc74ed9 --- /dev/null +++ b/.config/awesome/ui/osd.lua @@ -0,0 +1,102 @@ +--- Heavy inspiration from a design made by https://github.com/tsukki9696 + +local awful = require "awful" +local beautiful = require "beautiful" +local gtimer = require "gears.timer" +local phosphor = require "assets.phosphor" +local qmath = require "quarrel.math" +local qui = require "quarrel.ui" +local qvars = require "quarrel.vars" +local rubato = require "lib.rubato" +local wibox = require "wibox" + +local osd = {} + +local widget = qui.popup { + ontop = true, + visible = false, + placement = function(d) + awful.placement.top(d, { + margins = { + top = beautiful.useless_gap * 2, + }, + }) + end, + minimum_height = qvars.char_height * 2, + -- minimum_width = awful.screen.focused().geometry.width / 2, + widget = { + { + widget = wibox.container.place, + + qui.icon { + icon = phosphor.speaker_simple_none_fill, + widget = { + forced_height = qvars.char_height * 1.2, + forced_width = qvars.char_height * 1.2, + id = "icon", + }, + }, + }, + { + widget = wibox.container.place, + { + widget = wibox.widget.progressbar, + max_value = 100, + value = 20, + forced_height = qvars.char_height / 1.5, + forced_width = qvars.expanded_bar_size + - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2) + - (qvars.char_height / 1.25 + qvars.padding) * 3, + color = qvars.colors.yellow, + background_color = qvars.colors.black, + shape = qvars.shape, + id = "progress", + }, + }, + + { + widget = wibox.widget.textbox, + text = "20%", + font = qui.font(1.2), + id = "percentage", + }, + layout = wibox.layout.fixed.horizontal, + spacing = qvars.big_padding, + }, +} + +local timer + +local anim = rubato.timed { + duration = qvars.anim_duration, + intro = qvars.anim_intro, + pos = 0, + subscribed = function(pos) + widget.opacity = pos + if pos == 0 then + widget.visible = false + elseif not widget.visible then + widget.visible = true + elseif pos == 1 then + timer:start() + end + end, +} + +timer = gtimer { + timeout = 1, + callback = function() + anim.target = 0 + end, + single_shot = true, +} + +function osd.notify(icon, value, max) + anim.target = 1 + widget:get_children_by_id("icon")[1].image = icon + widget:get_children_by_id("progress")[1].value = value + widget:get_children_by_id("progress")[1].max_value = max + widget:get_children_by_id("percentage")[1].text = tostring(qmath.percentage(value, max)) .. "%" +end + +return osd diff --git a/.config/awesome/ui/powermenu/init.lua b/.config/awesome/ui/powermenu/init.lua new file mode 100644 index 0000000..81d2ea3 --- /dev/null +++ b/.config/awesome/ui/powermenu/init.lua @@ -0,0 +1,114 @@ +local awful = require "awful" +local phosphor = require "assets.phosphor" +local qui = require "quarrel.ui" +local qvars = require "quarrel.vars" +local rubato = require "lib.rubato" +local wibox = require "wibox" + +local function create_button(title, icon, color, exec) + return wibox.widget { + widget = wibox.container.background, + shape = qvars.shape, + { + widget = wibox.container.margin, + margins = qvars.big_padding, + { + qui.styled { + widget = wibox.container.background, + border_color = color, + { + widget = wibox.container.margin, + margins = qvars.big_padding, + qui.icon { + widget = { + forced_height = qvars.char_height * 4 - qvars.big_padding * 2, + forced_width = qvars.char_height * 4 - qvars.big_padding * 2, + }, + icon = icon, + color = color, + }, + }, + }, + { + widget = wibox.container.place, + { + widget = wibox.widget.textbox, + text = title, + }, + }, + layout = wibox.layout.fixed.vertical, + spacing = qvars.big_padding, + exec = exec, + }, + }, + select = function(self) + self.bg = qvars.colors.black + end, + } +end + +local powermenu = {} +local toggled = false + +screen.connect_signal("request::desktop_decoration", function(s) + powermenu._widget = qui.popup { + ontop = true, + visible = false, + minimum_width = s.geometry.width, + minimum_height = s.geometry.height, + bg = qvars.colors.bg .. "ee", + border_width = 0, + widget = { + widget = wibox.container.place, + qui.styled { + widget = wibox.container.background, + { + widget = wibox.container.margin, + margins = qvars.big_padding, + { + layout = wibox.layout.fixed.horizontal, + spacing = qvars.big_padding * 2, + id = "list", + }, + }, + }, + }, + } + + local layout = powermenu._widget.widget:get_children_by_id("list")[1] + + layout:add(create_button("Shutdown", phosphor.power_bold, qvars.colors.red, function() + awful.spawn "poweroff" + end)) + layout:add(create_button("Log out", phosphor.sign_out_bold, qvars.colors.green, function() + awesome.quit() + end)) + layout:add(create_button("Lock", phosphor.lock_simple_fill, qvars.colors.blue, function() + require("quarrel").debug "locked!l" + end)) + layout:add(create_button("Restart", phosphor.arrow_clockwise_bold, qvars.colors.pink, function() + awful.spawn "reboot" + end)) +end) + +local timed = rubato.timed { + duration = qvars.anim_duration, + intro = qvars.anim_intro, + pos = 0, + subscribed = function(pos) + powermenu._widget.opacity = pos + + if pos == 0 then + powermenu._widget.visible = false + else + powermenu._widget.visible = true + end + end, +} + +function powermenu:toggle() + timed.target = toggled and 0 or 1 + toggled = not toggled +end + +return powermenu diff --git a/.config/awesome/ui/statusbar/init.lua b/.config/awesome/ui/statusbar/init.lua index d4f2001..77a28f7 100644 --- a/.config/awesome/ui/statusbar/init.lua +++ b/.config/awesome/ui/statusbar/init.lua @@ -1,9 +1,8 @@ local awful = require "awful" local beautiful = require "beautiful" -local gcolor = require "gears.color" local panel = require "ui.statusbar.panel" local phosphor = require "assets.phosphor" -local qbind = require "quarrel.bind" +local qstore = require "quarrel.store" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" local rubato = require "lib.rubato" @@ -13,38 +12,26 @@ local clock = require "ui.statusbar.widgets.clock" local displays = require "ui.statusbar.widgets.displays" local keyboardlayout = require "ui.statusbar.widgets.keyboardlayout" local taglist = require "ui.statusbar.widgets.taglist" +local tasklist = require "ui.statusbar.widgets.tasklist" screen.connect_signal("request::desktop_decoration", function(s) - local expand_button = wibox.widget { - widget = wibox.container.place, - valign = "center", - halign = "center", - { - widget = wibox.widget.imagebox, - image = gcolor.recolor_image(phosphor.caret_right_fill, qvars.colors.fg), - forced_width = qvars.char_height, - forced_height = qvars.char_height - }, - _expanded = false - } - local bar = qui.popup { placement = function(d) - local place = awful.placement.left - return place(d, { - margins = beautiful.useless_gap * 2 + return awful.placement.left(d, { + margins = beautiful.useless_gap * 2, }) end, minimum_height = s.geometry.height - (beautiful.useless_gap * 4 + qvars.border_width * 2), widget = { { { - expand_button, - taglist, + taglist(s), layout = wibox.layout.fixed.vertical, spacing = qvars.padding * 2, + id = "top", }, nil, + -- tasklist(s), { widget = wibox.container.place, valign = "bottom", @@ -55,11 +42,16 @@ screen.connect_signal("request::desktop_decoration", function(s) displays.wifi, { widget = wibox.container.place, - keyboardlayout + { + widget = wibox.container.constraint, + height = qvars.char_height, + width = qvars.char_height, + keyboardlayout, + }, }, clock, layout = wibox.layout.fixed.vertical, - spacing = qvars.padding * 2 + spacing = qvars.padding * 2, }, }, layout = wibox.layout.align.vertical, @@ -74,23 +66,24 @@ screen.connect_signal("request::desktop_decoration", function(s) local bar_width = bar.width + qvars.border_width * 2 bar:struts { - left = bar_width + beautiful.useless_gap * 4 + left = bar_width + beautiful.useless_gap * 4, } local panel_width + local panel_toggle = { toggled = false } -- hacky but it works local timed = rubato.timed { duration = qvars.anim_duration, intro = qvars.anim_intro, pos = bar_width, subscribed = function(pos) - if pos ~= bar_width and expand_button._expanded then + if pos ~= bar_width and panel_toggle.toggled then bar.widget.widget.third = panel if panel_width == nil then panel_width = bar.widget.widget.third.width end bar.ontop = true - elseif pos == bar_width and not expand_button._expanded then + elseif pos == bar_width and not panel_toggle.toggled then bar.widget.widget.third = nil bar.ontop = false end @@ -98,25 +91,21 @@ screen.connect_signal("request::desktop_decoration", function(s) bar.shape = function(cr, _, h) qvars.shape(cr, pos, h) end - end + end, } - expand_button.buttons = { - qbind:new { - triggers = qvars.btns.left, - press = function() - if expand_button._expanded then - timed.target = bar_width - else - timed.target = bar_width + qvars.expanded_bar_size - -- timed.target = bar_width + qvars.big_padding + (qvars.big_padding * 2 + qvars.element_size * 4) * 3 + qvars.padding * 2 - end - - -- q.debug(tostring(panel_width)) - expand_button._expanded = not expand_button._expanded - end, - hidden = true - } + panel_toggle = qui.toggle { + off = phosphor.arrows_out_simple_fill, + on = phosphor.arrows_in_simple_fill, + press = function(self) + if not self.toggled then + timed.target = bar_width + else + timed.target = bar_width + qvars.expanded_bar_size + end + end, } - + + bar.widget:get_children_by_id("top")[1]:insert(1, panel_toggle) + qstore.panel_toggle = panel_toggle end) diff --git a/.config/awesome/ui/statusbar/panel/init.lua b/.config/awesome/ui/statusbar/panel/init.lua index 7685451..31c2860 100644 --- a/.config/awesome/ui/statusbar/panel/init.lua +++ b/.config/awesome/ui/statusbar/panel/init.lua @@ -1,6 +1,8 @@ local qvars = require "quarrel.vars" local wibox = require "wibox" +local battery = require "ui.statusbar.panel.widgets.battery" +local calendar = require "ui.statusbar.panel.widgets.calendar" local displays = require "ui.statusbar.panel.widgets.displays" local music = require "ui.statusbar.panel.widgets.music" local power_menu = require "ui.statusbar.panel.widgets.power_menu" @@ -9,7 +11,7 @@ local wifi = require "ui.statusbar.panel.widgets.wifi" local panel = wibox.widget { widget = wibox.container.margin, margins = { - left = qvars.big_padding + left = qvars.big_padding, }, { { @@ -24,25 +26,27 @@ local panel = wibox.widget { spacing = qvars.padding, }, wifi, - music, + -- battery, + -- music, + calendar, layout = wibox.layout.fixed.vertical, - spacing = qvars.padding - } + spacing = qvars.padding, + }, }, { widget = wibox.container.background, { widget = wibox.widget.textbox, - text = ":)" - } + text = ":)", + }, }, { widget = wibox.container.place, valign = "bottom", - power_menu + power_menu, }, layout = wibox.layout.align.vertical, - } + }, } return panel diff --git a/.config/awesome/ui/statusbar/panel/widgets/battery.lua b/.config/awesome/ui/statusbar/panel/widgets/battery.lua new file mode 100644 index 0000000..52685b7 --- /dev/null +++ b/.config/awesome/ui/statusbar/panel/widgets/battery.lua @@ -0,0 +1,70 @@ +local gears = require "gears" +local lit = require "lib.lit" +local qbezier = require "quarrel.bezier" +local qui = require "quarrel.ui" +local wibox = require "wibox" + +local widget = wibox.widget { + widget = wibox.container.constraint, + height = 120, + width = 120, + strategy = "exact", + { + widget = lit.widget.linechart, + -- max_value = 30, + -- step_width = 10, + -- scale = true, + -- stack = false, + -- forced_height = 30, + -- step_hook = curvaceous + -- values = { 1, 10, 100 }, + fill = true, + fill_color = "#00ff00", + -- fill_border = true, + fill_border_color = "#ff0000", + draw_bg = function(cr, width, _, min, max, transform) + cr:set_line_width(1) + local min_abs = math.abs(min) + local dash_pattern = { 6, 4 } + local rendered_zero, zero_overlap = false, false + for i = 0, 4 do + local temp = ((min_abs + max) * (i / 4)) - min_abs + rendered_zero = rendered_zero or temp == 0 + if rendered_zero and not zero_overlap and min < 0 then + cr:set_source_rgba(gears.color.parse_color "#0000ff") + zero_overlap = true + else + cr:set_dash(dash_pattern, 1, 0) + cr:set_source_rgba(gears.color.parse_color "#ff0000ff") + end + local y = transform(temp) + cr:move_to(0, y) + cr:line_to(width, y) + cr:stroke() + cr:set_dash({}, 0, 0) + end + + if not rendered_zero then + cr:set_source_rgba(gears.color.parse_color "#0000ff") + local h_0 = transform(0) + cr:move_to(0, h_0) + cr:line_to(width, h_0) + cr:stroke() + end + end, + }, +} + +-- for _ = 1, 100 do +-- widget.widget:add_value(math.random(0, 30)) +-- end + +-- require "gears.timer" { +-- autostart = true, +-- timeout = 0.2, +-- callback = function () +-- widget.widget:add_value(math.random(0, 30)) +-- end +-- } + +return widget diff --git a/.config/awesome/ui/statusbar/panel/widgets/calendar.lua b/.config/awesome/ui/statusbar/panel/widgets/calendar.lua index 8b13789..8933543 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/calendar.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/calendar.lua @@ -1 +1,156 @@ +local awful = require "awful" +local phosphor = require "assets.phosphor" +local qbind = require "quarrel.bind" +local qmarkup = require "quarrel.markup" +local qui = require "quarrel.ui" +local qvars = require "quarrel.vars" +local wibox = require "wibox" +local weekday_map = { 7, 1, 2, 3, 4, 5, 6 } + +local calendar = wibox.widget(qui.styled { + widget = wibox.container.background, + { + widget = wibox.container.margin, + margins = qvars.big_padding, + { + { + { + widget = wibox.container.place, + qui.icon { + icon = phosphor.caret_left_bold, + }, + }, + { + widget = wibox.container.place, + { + widget = wibox.widget.textbox, + text = "January 2024", + }, + }, + { + widget = wibox.container.place, + qui.icon { + icon = phosphor.caret_right_bold, + }, + }, + layout = wibox.layout.align.horizontal, + expand = "outside", + }, + { + layout = wibox.layout.grid, + forced_num_rows = 7, + forced_num_cols = 7, + spacing = qvars.padding, + id = "grid", + }, + layout = wibox.layout.fixed.vertical, + spacing = qvars.big_padding, + }, + }, +}) + +-- Logic heavily inspired by https://github.com/Sinomor/dotfiles/blob/e409f9a84bf40daf1e39c0179ec749232ed827c9/home/.config/awesome/ui/control/moment/calendar.lua#L134-L173 +function calendar:compute_grid(year, month) + local calendar_table = { {}, {}, {}, {}, {}, {} } + local current = os.date("*t", os.time()) + local first_day = os.date("*t", os.time { year = year, month = month, day = 1 }) + local last_day = os.date("*t", os.time { year = year, month = month + 1, day = 0 }) + + local days = last_day.day + + local prev_days = os.date("*t", os.time { year = year, month = month, day = 0 }) + local prev_offset = weekday_map[first_day.wday] - 1 + local prev_month = month - 1 == -1 and 12 or month - 1 + local prev_year = prev_month == 12 and year - 1 or year + + if prev_offset ~= -1 then + for offset = prev_offset, 1, -1 do + local day = prev_days.day - offset + 1 + table.insert( + calendar_table[1], + { day, day == current.day and prev_month == current.month and prev_year == current.year, false } + ) + end + end + + do + local row = 1 + local weekday = weekday_map[first_day.wday] + for day = 1, days do + table.insert( + calendar_table[row], + { day, day == current.day and month == current.month and year == current.year, true } + ) + if weekday == 7 then + row = row + 1 + weekday = 1 + else + weekday = weekday + 1 + end + end + + local next_month = month + 1 == 13 and 1 or month + 1 + local next_year = next_month == 1 and year + 1 or year + for day = 1, 42 - prev_offset - days do + table.insert( + calendar_table[row], + { day, day == current.day and next_month + 1 == current.month and next_year == current.year, false } + ) + if weekday == 7 then + row = row + 1 + weekday = 1 + else + weekday = weekday + 1 + end + end + end + + for i, row in ipairs(calendar_table) do + for j, col in ipairs(row) do + local widget = self:get_children_by_id("grid")[1]:get_widgets_at(i + 1, j)[1] + widget.widget.widget.text = col[1] + if col[2] then + widget.bg = qvars.colors.yellow + widget.fg = qvars.colors.bg + else + widget.bg = qvars.colors.bg + widget.fg = col[3] and qvars.colors.fg or qvars.colors.dim.fg + end + end + end +end + +local cells = {} +local function cell(content) + local widget = wibox.widget { + widget = wibox.container.background, + shape = qvars.shape, + { + widget = wibox.container.place, + forced_height = qvars.char_height * 1.5, + forced_width = qvars.char_height * 1.5, + { + widget = wibox.widget.textbox, + markup = content, + }, + }, + } + table.insert(cells, widget) +end + +cell "Mo" +cell "Tu" +cell "We" +cell "Th" +cell "Fr" +cell(qmarkup("Sa", { bold = true })) +cell(qmarkup("Su", { bold = true })) + +for _ = 1, 42 do + cell() +end +calendar:get_children_by_id("grid")[1]:add(table.unpack(cells)) +calendar:compute_grid(2024, 1) + +return calendar diff --git a/.config/awesome/ui/statusbar/panel/widgets/displays.lua b/.config/awesome/ui/statusbar/panel/widgets/displays.lua index de15bd5..b52c986 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/displays.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/displays.lua @@ -1,3 +1,5 @@ +local backlight = require "services.backlight" +local battery = require "services.battery" local phosphor = require "assets.phosphor" local qmath = require "quarrel.math" local qui = require "quarrel.ui" @@ -23,23 +25,29 @@ local function create_display(icon, color) forced_width = qvars.element_size * 4, { widget = wibox.container.place, - qui.icon(icon, color, { id = "icon" }) + qui.icon { + icon = icon, + color = color, + widget = { + id = "icon", + }, + }, }, - id = "indicator" + id = "indicator", }, { widget = wibox.container.place, { widget = wibox.widget.textbox, text = "0%", - id = "text" - } + id = "text", + }, }, layout = wibox.layout.fixed.vertical, - spacing = qvars.big_padding - } - } - } + spacing = qvars.big_padding, + }, + }, + }, }) end @@ -58,23 +66,23 @@ awesome.connect_signal("services::audio::icon", function(icon, color) end) local d_battery = create_display(phosphor.battery_vertical_warning_fill, qvars.colors.red) -awesome.connect_signal("services::battery", function(capacity) - d_battery:get_children_by_id("indicator")[1].value = capacity - d_battery:get_children_by_id("text")[1].text = capacity .. "%" +battery:connect_signal("level", function(_, level) + d_battery:get_children_by_id("indicator")[1].value = level + d_battery:get_children_by_id("text")[1].text = level .. "%" end) -awesome.connect_signal("services::battery::icon", function(icon, color) +battery:connect_signal("icon", function(_, icon, color) d_battery:get_children_by_id("indicator")[1].color = color d_battery:get_children_by_id("icon")[1].image = icon d_battery:get_children_by_id("icon")[1].stylesheet = qui.recolor(color) end) local d_brightness = create_display(phosphor.sun_fill, qvars.colors.fg) -awesome.connect_signal("services::brightness", function(brightness) +backlight:connect_signal("value", function(_, brightness) brightness = math.floor(qmath.translate_range(brightness, 0, 255, 0, 100)) d_brightness:get_children_by_id("indicator")[1].value = brightness d_brightness:get_children_by_id("text")[1].text = brightness .. "%" end) -awesome.connect_signal("services::brightness::icon", function(icon, color) +backlight:connect_signal("icon", function(_, icon, color) d_brightness:get_children_by_id("icon")[1].image = icon d_brightness:get_children_by_id("icon")[1].stylesheet = qui.recolor(color) end) diff --git a/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua b/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua index 8c6e8a5..5caadc5 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua @@ -24,13 +24,13 @@ -- @supermodule wibox.widget.base --------------------------------------------------------------------------- -local lgi = require("lgi") +local lgi = require "lgi" local cairo = lgi.cairo -local base = require("wibox.widget.base") -local surface = require("gears.surface") -local gtable = require("gears.table") -local gdebug = require("gears.debug") +local base = require "wibox.widget.base" +local gdebug = require "gears.debug" +local gtable = require "gears.table" +local surface = require "gears.surface" local setmetatable = setmetatable local type = type local math = math @@ -40,7 +40,9 @@ local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility -- Safe load for optional Rsvg module local Rsvg = nil do - local success, err = pcall(function() Rsvg = lgi.Rsvg end) + local success, err = pcall(function() + Rsvg = lgi.Rsvg + end) if not success then gdebug.print_warning(debug.traceback("Could not load Rsvg: " .. tostring(err))) end @@ -48,14 +50,16 @@ end local imagebox = { mt = {} } -local rsvg_handle_cache = setmetatable({}, { __mode = 'k' }) +local rsvg_handle_cache = setmetatable({}, { __mode = "k" }) ---Load rsvg handle form image file -- @tparam string file Path to svg file. -- @return Rsvg handle -- @treturn table A table where cached data can be stored. local function load_rsvg_handle(file) - if not Rsvg then return end + if not Rsvg then + return + end local cache = (rsvg_handle_cache[file] or {})["handle"] @@ -65,7 +69,7 @@ local function load_rsvg_handle(file) local handle, err - if file:match("<[?]?xml") or file:match("<svg") then + if file:match "<[?]?xml" or file:match "<svg" then handle, err = Rsvg.Handle.new_from_data(file) else handle, err = Rsvg.Handle.new_from_file(file) @@ -81,7 +85,9 @@ end ---Apply cairo surface for given imagebox widget local function set_surface(ib, surf) local is_surf_valid = surf.width > 0 and surf.height > 0 - if not is_surf_valid then return false end + if not is_surf_valid then + return false + end ib._private.default = { width = surf.width, height = surf.height } ib._private.handle = nil @@ -93,7 +99,9 @@ end local function set_handle(ib, handle, cache) local dim = handle:get_dimensions() local is_handle_valid = dim.width > 0 and dim.height > 0 - if not is_handle_valid then return false end + if not is_handle_valid then + return false + end ib._private.default = { width = dim.width, height = dim.height } ib._private.handle = handle @@ -124,18 +132,15 @@ end -- It's necessary because a single RSVG handle can be used by -- many imageboxes. So DPI and Stylesheet need to be set each time. local function update_dpi(self, ctx) - if not self._private.handle then return end + if not self._private.handle then + return + end - local dpi = self._private.auto_dpi and - ctx.dpi or - self._private.dpi or - nil + local dpi = self._private.auto_dpi and ctx.dpi or self._private.dpi or nil - local need_dpi = dpi and - self._private.last_dpi ~= dpi + local need_dpi = dpi and self._private.last_dpi ~= dpi - local need_style = self._private.handle.set_stylesheet and - self._private.stylesheet + local need_style = self._private.handle.set_stylesheet and self._private.stylesheet local old_size = self._private.default and self._private.default.width @@ -163,14 +168,16 @@ local function update_dpi(self, ctx) -- This can happen in the constructor when `dpi` is set after `image`. if old_size and old_size ~= self._private.default.width then - self:emit_signal("widget::redraw_needed") - self:emit_signal("widget::layout_changed") + self:emit_signal "widget::redraw_needed" + self:emit_signal "widget::layout_changed" end end -- Draw an imagebox with the given cairo context in the given geometry. function imagebox:draw(ctx, cr, width, height) - if width == 0 or height == 0 or not self._private.default then return end + if width == 0 or height == 0 or not self._private.default then + return + end -- For valign = "top" and halign = "left" local translate = { @@ -186,15 +193,15 @@ function imagebox:draw(ctx, cr, width, height) -- That's for the "fit" policy. local aspects = { w = width / w, - h = height / h + h = height / h, } local policy = { w = self._private.horizontal_fit_policy or "auto", - h = self._private.vertical_fit_policy or "auto" + h = self._private.vertical_fit_policy or "auto", } - for _, aspect in ipairs {"w", "h"} do + for _, aspect in ipairs { "w", "h" } do if self._private.upscale == false and (w < width and h < height) then aspects[aspect] = 1 elseif self._private.downscale == false and (w >= width and h >= height) then @@ -209,15 +216,15 @@ function imagebox:draw(ctx, cr, width, height) end if self._private.halign == "center" then - translate.x = math.floor((width - w*aspects.w)/2) + translate.x = math.floor((width - w * aspects.w) / 2) elseif self._private.halign == "right" then - translate.x = math.floor(width - (w*aspects.w)) + translate.x = math.floor(width - (w * aspects.w)) end if self._private.valign == "center" then - translate.y = math.floor((height - h*aspects.h)/2) + translate.y = math.floor((height - h * aspects.h) / 2) elseif self._private.valign == "bottom" then - translate.y = math.floor(height - (h*aspects.h)) + translate.y = math.floor(height - (h * aspects.h)) end cr:translate(translate.x, translate.y) @@ -226,25 +233,25 @@ function imagebox:draw(ctx, cr, width, height) local threshold, max_factor = self._private.max_scaling_factor, math.max(aspects.w, aspects.h) if threshold and threshold > 0 and threshold < max_factor then - aspects.w = (aspects.w*threshold)/max_factor - aspects.h = (aspects.h*threshold)/max_factor + aspects.w = (aspects.w * threshold) / max_factor + aspects.h = (aspects.h * threshold) / max_factor end -- Set the clip if self._private.clip_shape then - cr:clip(self._private.clip_shape(cr, w*aspects.w, h*aspects.h, unpack(self._private.clip_args))) + cr:clip(self._private.clip_shape(cr, w * aspects.w, h * aspects.h, unpack(self._private.clip_args))) end cr:scale(aspects.w, aspects.h) else if self._private.halign == "center" then - translate.x = math.floor((width - w)/2) + translate.x = math.floor((width - w) / 2) elseif self._private.halign == "right" then translate.x = math.floor(width - w) end if self._private.valign == "center" then - translate.y = math.floor((height - h)/2) + translate.y = math.floor((height - h) / 2) elseif self._private.valign == "bottom" then translate.y = math.floor(height - h) end @@ -274,7 +281,9 @@ end -- Fit the imagebox into the given geometry function imagebox:fit(ctx, width, height) - if not self._private.default then return 0, 0 end + if not self._private.default then + return 0, 0 + end update_dpi(self, ctx) @@ -349,11 +358,13 @@ function imagebox:set_image(image) self._private.default = nil end - if not setup_succeed then return false end + if not setup_succeed then + return false + end - self:emit_signal("widget::redraw_needed") - self:emit_signal("widget::layout_changed") - self:emit_signal("property::image") + self:emit_signal "widget::redraw_needed" + self:emit_signal "widget::layout_changed" + self:emit_signal "property::image" return true end @@ -383,8 +394,8 @@ end -- @see clip_shape function imagebox:set_clip_shape(clip_shape, ...) self._private.clip_shape = clip_shape - self._private.clip_args = {...} - self:emit_signal("widget::redraw_needed") + self._private.clip_args = { ... } + self:emit_signal "widget::redraw_needed" self:emit_signal("property::clip_shape", clip_shape) end @@ -468,14 +479,14 @@ end -- @propemits true false -- @see dpi -for _, prop in ipairs {"stylesheet", "dpi", "auto_dpi"} do +for _, prop in ipairs { "stylesheet", "dpi", "auto_dpi" } do imagebox["set_" .. prop] = function(self, value) -- It will be set in :fit and :draw. The handle is shared -- by multiple imagebox, so it cannot be set just once. self._private[prop] = value - self:emit_signal("widget::redraw_needed") - self:emit_signal("widget::layout_changed") + self:emit_signal "widget::redraw_needed" + self:emit_signal "widget::layout_changed" self:emit_signal("property::" .. prop) end end @@ -490,12 +501,12 @@ function imagebox:set_resize(allowed) self:emit_signal("property::upscale", allowed) end - self:emit_signal("widget::redraw_needed") - self:emit_signal("widget::layout_changed") + self:emit_signal "widget::redraw_needed" + self:emit_signal "widget::layout_changed" self:emit_signal("property::resize", allowed) end -for _, prop in ipairs {"downscale", "upscale" } do +for _, prop in ipairs { "downscale", "upscale" } do imagebox["set_" .. prop] = function(self, allowed) self._private[prop] = allowed @@ -504,9 +515,9 @@ for _, prop in ipairs {"downscale", "upscale" } do self:emit_signal("property::resize", self._private.resize) end - self:emit_signal("widget::redraw_needed") - self:emit_signal("widget::layout_changed") - self:emit_signal("property::"..prop, allowed) + self:emit_signal "widget::redraw_needed" + self:emit_signal "widget::layout_changed" + self:emit_signal("property::" .. prop, allowed) end end @@ -542,7 +553,6 @@ end -- @see horizontal_fit_policy -- @see resize - --- The vertical alignment. -- -- @DOC_wibox_widget_imagebox_valign_EXAMPLE@ @@ -627,30 +637,34 @@ end -- @see max_scaling_factor local defaults = { - halign = "left", - valign = "top", + halign = "left", + valign = "top", horizontal_fit_policy = "auto", - vertical_fit_policy = "auto", - max_scaling_factor = 0, - scaling_quality = "good" + vertical_fit_policy = "auto", + max_scaling_factor = 0, + scaling_quality = "good", } local function get_default(prop, value) - if value == nil then return defaults[prop] end + if value == nil then + return defaults[prop] + end return value end for prop in pairs(defaults) do - imagebox["set_"..prop] = function(self, value) - if value == self._private[prop] then return end + imagebox["set_" .. prop] = function(self, value) + if value == self._private[prop] then + return + end self._private[prop] = get_default(prop, value) - self:emit_signal("widget::redraw_needed") - self:emit_signal("property::"..prop, self._private[prop]) + self:emit_signal "widget::redraw_needed" + self:emit_signal("property::" .. prop, self._private[prop]) end - imagebox["get_"..prop] = function(self) + imagebox["get_" .. prop] = function(self) if self._private[prop] == nil then return defaults[prop] end @@ -678,7 +692,7 @@ end -- @treturn wibox.widget.imagebox A new `wibox.widget.imagebox` widget instance. -- @constructorfct wibox.widget.imagebox local function new(image, resize_allowed, clip_shape, ...) - local ret = base.make_widget(nil, nil, {enable_properties = true}) + local ret = base.make_widget(nil, nil, { enable_properties = true }) gtable.crush(ret, imagebox, true) ret._private.resize = true @@ -692,7 +706,7 @@ local function new(image, resize_allowed, clip_shape, ...) end ret._private.clip_shape = clip_shape - ret._private.clip_args = {...} + ret._private.clip_args = { ... } return ret end diff --git a/.config/awesome/ui/statusbar/panel/widgets/linegraph.lua b/.config/awesome/ui/statusbar/panel/widgets/linegraph.lua deleted file mode 100644 index 9a42632..0000000 --- a/.config/awesome/ui/statusbar/panel/widgets/linegraph.lua +++ /dev/null @@ -1,154 +0,0 @@ -local gcolor = require "gears.color" -local gtable = require "gears.table" -local wibox = require "wibox" - -local linegraph = { mt = {} } - -function linegraph:fit(_, width, height) - if #self._private.values < 2 then - return 0, 0 - end - return width, height -end - -function linegraph:draw(_, cr, width, height) - if #self._private.values < 2 then - return - end - local line_width = self._private.line_width - local values = self._private.values - - local max, min = self._private.max or 0, self._private.min or 0 - if not (self._private.max and self._private.min) then - for _, value in ipairs(values) do - if not self._private.max then - max = max < value and value or max - end - if not self._private.min then - min = min > value and value or min - end - end - end - - local usable_height = height - line_width - local min_abs = math.abs(min) - local h_factor = usable_height / (min_abs + max) - - local function transform(value) - return usable_height - math.floor((value + min_abs) * h_factor) + line_width / 2 - end - - local graph_values = {} - for i = 1, #values, math.ceil(#values / width) do - table.insert(graph_values, transform(values[i])) - end - - if self._private.draw_bg then - cr:save() - self._private.draw_bg(cr, width, height, min, max, transform) - cr:restore() - end - - cr:set_line_width(line_width) - cr:set_source(gcolor(self.color)) - cr:move_to(line_width / 2, graph_values[1]) - for i = 2, #graph_values do - cr:line_to((width - line_width / 2) / (#graph_values - 1) * (i - 1), graph_values[i]) - end - - if self._private.fill then - cr:line_to(width - line_width / 2, transform(min)) - cr:line_to(line_width / 2, transform(min)) - cr:line_to(line_width / 2, graph_values[1]) - cr:stroke_preserve() - cr:set_source(gcolor(self.fill_color)) - cr:fill() - else - cr:stroke() - end -end - -function linegraph:set_values(values) - self._private.values = values - self:emit_signal "widget::redraw_needed" -end - -function linegraph:get_values() - return self._private.values -end - -function linegraph:set_draw_bg(draw_bg) - self._private.draw_bg = draw_bg - self:emit_signal "widget::redraw_needed" -end - -function linegraph:get_draw_bg() - return self._private.draw_bg -end - -function linegraph:set_max(max) - self._private.max = max - self:emit_signal "widget::redraw_needed" -end - -function linegraph:get_max() - return self._private.max -end - -function linegraph:set_min(min) - self._private.max = min - self:emit_signal "widget::redraw_needed" -end - -function linegraph:get_min() - return self._private.min -end - -function linegraph:set_line_width(line_width) - self._private.line_width = line_width - self:emit_signal "widget::redraw_needed" -end - -function linegraph:set_color(color) - self._private.color = color - self:emit_signal "widget::redraw_needed" -end - -function linegraph:get_color() - return self._private.color -end - -function linegraph:set_fill_color(fill_color) - self._private.fill_color = fill_color - self:emit_signal "widget::redraw_needed" -end - -function linegraph:get_fill_color() - return self._private.fill_color -end - -function linegraph:set_fill(fill) - self._private.fill = fill - self:emit_signal "widget::redraw_needed" -end - -function linegraph:get_fill() - return self._private.fill -end - -local function new() - local ret = wibox.widget.base.make_widget(nil, nil, { enable_properties = true }) - gtable.crush(ret, linegraph, true) - - ret.line_width = 1 - ret.color = "#ffffff" - ret.fill_color = "#000000" - - return ret -end - -function linegraph.mt:__call() - return new() -end - -return setmetatable(linegraph, linegraph.mt) diff --git a/.config/awesome/ui/statusbar/panel/widgets/music.lua b/.config/awesome/ui/statusbar/panel/widgets/music.lua index 3eedb55..eea7335 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/music.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/music.lua @@ -1,28 +1,10 @@ -local cairo = require "lgi".cairo -local gcolor = require "gears.color" -local gsurface = require "gears.surface" -local imagebox = require "ui.statusbar.panel.widgets.imagebox" -local phosphor = require "assets.phosphor" -local playerctl = require "services.playerctl" local qnative = require "quarrel.native" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" local wibox = require "wibox" -local default_cover = phosphor.vinyl_record_fill local default_text = "Nothing playing" -local function faded_cover(cover) - local surface = gsurface(cover) - local w,h = gsurface.get_size(surface) - local cr = cairo.Context(surface) - local pattern = gcolor(qvars.colors.bg .. "aa") - cr:rectangle(0, 0, w, h) - cr:set_source(pattern) - cr:fill_preserve() - return surface -end - local w_title = wibox.widget { widget = wibox.widget.textbox, text = "Nothing playing", @@ -33,20 +15,8 @@ local w_artist = wibox.widget { fg = qvars.colors.dim.fg, { widget = wibox.widget.textbox, - text = "" - } -} - -local w_cover = wibox.widget { - widget = imagebox, - image = default_cover, - stylesheet = qui.recolor(qvars.colors.bright.black), - forced_height = qvars.char_height * 6 + qvars.big_padding * 2, - forced_width = qvars.expanded_bar_size - qvars.big_padding, - horizontal_fit_policy = "cover", - vertical_fit_policy = "cover", - valign = "center", - halign = "center" + text = "", + }, } local w_progress_bar = wibox.widget { @@ -54,100 +24,22 @@ local w_progress_bar = wibox.widget { max_value = 0, value = 0, forced_height = qvars.char_height / 2, - forced_width = qvars.expanded_bar_size - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2) - (qvars.char_height / 1.25 + qvars.padding) * 3, + forced_width = qvars.expanded_bar_size + - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2) + - (qvars.char_height / 1.25 + qvars.padding) * 3, color = qvars.colors.yellow, background_color = qvars.colors.black, shape = qvars.shape, } -local w_play_pause = qui.toggle { - widget = { - forced_height = qvars.char_height / 1.25, - forced_width = qvars.char_height / 1.25 - }, - off = phosphor.play_fill, - on = phosphor.pause_fill, - manual = true, - press = function() - playerctl:play_pause() - end -} - -local w_skip_forward = qui.button { - widget = { - forced_height = qvars.char_height / 1.25, - forced_width = qvars.char_height / 1.25 - }, - image = phosphor.skip_forward_fill, - press = function() - playerctl:next() - end -} - -local w_skip_back = qui.button { - widget = { - forced_height = qvars.char_height / 1.25, - forced_width = qvars.char_height / 1.25 - }, - image = phosphor.skip_back_fill, - press = function() - playerctl:previous() - end -} - local music = wibox.widget(qui.styled { widget = wibox.container.background, { + widget = wibox.container.margin, + margins = qvars.big_padding, { - widget = wibox.container.background, - bg = qvars.colors.black - }, - w_cover, - { - widget = wibox.container.margin, - margins = qvars.big_padding, { { - { - widget = wibox.container.background, - bg = qvars.colors.bg, - shape = qvars.shape, - { - widget = wibox.container.margin, - margins = qvars.padding, - { - { - widget = wibox.container.constraint, - width = qvars.expanded_bar_size - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2), - height = qvars.char_height, - { - widget = wibox.container.scroll.horizontal, - speed = 50, - step_function = wibox.container.scroll.step_functions.waiting_nonlinear_back_and_forth, - w_title - } - }, - { - widget = wibox.container.constraint, - width = qvars.expanded_bar_size - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2), - height = qvars.char_height, - { - widget = wibox.container.scroll.horizontal, - speed = 50, - step_function = wibox.container.scroll.step_functions.waiting_nonlinear_back_and_forth, - w_artist - } - }, - layout = wibox.layout.fixed.vertical - } - } - }, - nil, - nil, - layout = wibox.layout.align.horizontal - }, - nil, - { widget = wibox.container.background, bg = qvars.colors.bg, shape = qvars.shape, @@ -155,29 +47,53 @@ local music = wibox.widget(qui.styled { widget = wibox.container.margin, margins = qvars.padding, { - w_play_pause, - w_skip_back, { - widget = wibox.container.place, - w_progress_bar + widget = wibox.container.constraint, + width = qvars.expanded_bar_size + - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2), + height = qvars.char_height, + { + widget = wibox.container.scroll.horizontal, + speed = 50, + step_function = wibox.container.scroll.step_functions.waiting_nonlinear_back_and_forth, + w_title, + }, }, - w_skip_forward, - layout = wibox.layout.fixed.horizontal, - spacing = qvars.padding - } - } + { + widget = wibox.container.constraint, + width = qvars.expanded_bar_size + - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2), + height = qvars.char_height, + { + widget = wibox.container.scroll.horizontal, + speed = 50, + step_function = wibox.container.scroll.step_functions.waiting_nonlinear_back_and_forth, + w_artist, + }, + }, + layout = wibox.layout.fixed.vertical, + }, + }, }, - layout = wibox.layout.align.vertical - } + nil, + nil, + layout = wibox.layout.align.horizontal, + }, + nil, + { + widget = wibox.container.background, + bg = qvars.colors.bg, + shape = qvars.shape, + w_progress_bar, + }, + layout = wibox.layout.align.vertical, }, - layout = wibox.layout.stack - } + }, }) awesome.connect_signal("services::playerctl::metadata", function(title, artist, album_path) - w_title.text = title ~= "" and qnative.decode_html(title) or default_text - w_artist.widget.text = qnative.decode_html(artist) - w_cover.image = faded_cover(album_path) + w_title.text = title ~= "" and qnative.util.decode_html(title) or default_text + w_artist.widget.text = qnative.util.decode_html(artist) end) awesome.connect_signal("services::playerctl::position", function(position, length) @@ -186,15 +102,10 @@ awesome.connect_signal("services::playerctl::position", function(position, lengt end) awesome.connect_signal("services::playerctl::no_players", function() - w_title = default_text - w_artist = "" - w_cover.image = default_cover + w_title.text = default_text + w_artist.text = "" w_progress_bar.value = 0 w_progress_bar.max_value = 0 end) -awesome.connect_signal("services::playerctl::playback_status", function(playing) - w_play_pause:silent_press(playing) -end) - return music diff --git a/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua b/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua index cf0de28..feea829 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua @@ -4,7 +4,7 @@ local qui = require "quarrel.ui" local qvars = require "quarrel.vars" local wibox = require "wibox" -local power_menu = wibox.widget { +return wibox.widget { qui.styled { widget = wibox.container.background, bg = qvars.colors.black, @@ -15,12 +15,12 @@ local power_menu = wibox.widget { qbind:new { triggers = qvars.btns.left, press = function() - q.debug("from 1") + q.debug "from 1" end, - hidden = true - } - } - } + hidden = true, + }, + }, + }, }, qui.styled { widget = wibox.container.background, @@ -32,12 +32,12 @@ local power_menu = wibox.widget { qbind:new { triggers = qvars.btns.left, press = function() - q.debug("from 2") + q.debug "from 2" end, - hidden = true - } - } - } + hidden = true, + }, + }, + }, }, qui.styled { widget = wibox.container.background, @@ -49,14 +49,12 @@ local power_menu = wibox.widget { qbind:new { triggers = qvars.btns.left, press = function() - q.debug("from 3") + q.debug "from 3" end, - hidden = true - } - } - } + hidden = true, + }, + }, + }, }, - layout = wibox.layout.flex.horizontal + layout = wibox.layout.flex.horizontal, } - -return power_menu diff --git a/.config/awesome/ui/statusbar/panel/widgets/wifi.lua b/.config/awesome/ui/statusbar/panel/widgets/wifi.lua index 579474e..ad2234f 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/wifi.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/wifi.lua @@ -1,7 +1,9 @@ +local lgi = require "lgi" local phosphor = require "assets.phosphor" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" local wibox = require "wibox" +local glib = lgi.GLib local wifi = wibox.widget(qui.styled { widget = wibox.container.background, @@ -13,17 +15,23 @@ local wifi = wibox.widget(qui.styled { widget = wibox.container.place, valign = "center", halign = "center", - qui.icon(phosphor.wifi_x_fill, qvars.colors.red, { id = "icon" }) + qui.icon { + icon = phosphor.wifi_x_fill, + color = qvars.colors.red, + widget = { + id = "icon", + }, + }, }, { widget = wibox.widget.textbox, text = "Disconnected", - id = "essid" + id = "essid", }, layout = wibox.layout.fixed.horizontal, - spacing = qvars.padding - } - } + spacing = qvars.padding, + }, + }, }) awesome.connect_signal("services::wifi", function(essid, _, connected) diff --git a/.config/awesome/ui/statusbar/widgets/clock.lua b/.config/awesome/ui/statusbar/widgets/clock.lua index ff57a36..c186dc5 100644 --- a/.config/awesome/ui/statusbar/widgets/clock.lua +++ b/.config/awesome/ui/statusbar/widgets/clock.lua @@ -1,13 +1,11 @@ local qvars = require "quarrel.vars" local wibox = require "wibox" -local clock = wibox.widget { +return wibox.widget { widget = wibox.container.place, { format = "%H\n%M", widget = wibox.widget.textclock, - font = qvars.font - } + font = qvars.font, + }, } - -return clock diff --git a/.config/awesome/ui/statusbar/widgets/displays.lua b/.config/awesome/ui/statusbar/widgets/displays.lua index 44ff2c6..62e4398 100644 --- a/.config/awesome/ui/statusbar/widgets/displays.lua +++ b/.config/awesome/ui/statusbar/widgets/displays.lua @@ -1,3 +1,4 @@ +local battery = require "services.battery" local phosphor = require "assets.phosphor" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" @@ -8,32 +9,35 @@ local function create_display(icon, color) widget = wibox.container.place, valign = "center", halign = "center", - qui.icon(icon, color) + qui.icon { + icon = icon, + color = color, + }, } end -local battery = create_display(phosphor.battery_vertical_warning_fill, qvars.colors.red) -awesome.connect_signal("services::battery::icon", function(icon, color) - battery.widget.image = icon - battery.widget.stylesheet = qui.recolor(color) +local d_battery = create_display(phosphor.battery_vertical_warning_fill, qvars.colors.red) +battery:connect_signal("icon", function(_, icon, color) + d_battery.widget.image = icon + d_battery.widget.stylesheet = qui.recolor(color) end) -local brightness = create_display(phosphor.moon_fill, qvars.colors.fg) +local d_brightness = create_display(phosphor.moon_fill, qvars.colors.fg) awesome.connect_signal("services::brightness::icon", function(icon, color) - brightness.widget.image = icon - brightness.widget.stylesheet = qui.recolor(color) + d_brightness.widget.image = icon + d_brightness.widget.stylesheet = qui.recolor(color) end) -local audio = create_display(phosphor.speaker_simple_slash_fill, qvars.colors.red) +local d_audio = create_display(phosphor.speaker_simple_slash_fill, qvars.colors.red) awesome.connect_signal("services::audio::icon", function(icon, color) - audio.widget.image = icon - audio.widget.stylesheet = qui.recolor(color) + d_audio.widget.image = icon + d_audio.widget.stylesheet = qui.recolor(color) end) -local wifi = create_display(phosphor.wifi_x_fill, qvars.colors.red) +local d_wifi = create_display(phosphor.wifi_x_fill, qvars.colors.red) awesome.connect_signal("services::wifi::icon", function(icon, color) - wifi.widget.image = icon - wifi.widget.stylesheet = qui.recolor(color) + d_wifi.widget.image = icon + d_wifi.widget.stylesheet = qui.recolor(color) end) -return { audio = audio, battery = battery, brightness = brightness, wifi = wifi } +return { audio = d_audio, battery = d_battery, brightness = d_brightness, wifi = d_wifi } diff --git a/.config/awesome/ui/statusbar/widgets/keyboardlayout.lua b/.config/awesome/ui/statusbar/widgets/keyboardlayout.lua index 0bbbf14..d9302ff 100644 --- a/.config/awesome/ui/statusbar/widgets/keyboardlayout.lua +++ b/.config/awesome/ui/statusbar/widgets/keyboardlayout.lua @@ -10,12 +10,12 @@ -- @supermodule wibox.widget.base --------------------------------------------------------------------------- -local capi = {awesome = awesome} +local capi = { awesome = awesome } local setmetatable = setmetatable -local textbox = require("wibox.widget.textbox") -local button = require("awful.button") -local widget_base = require("wibox.widget.base") -local gdebug = require("gears.debug") +local button = require "awful.button" +local gdebug = require "gears.debug" +local textbox = require "wibox.widget.textbox" +local widget_base = require "wibox.widget.base" --- Keyboard Layout widget. -- awful.widget.keyboardlayout @@ -24,109 +24,109 @@ local keyboardlayout = { mt = {} } -- As to the country-code-like symbols below, refer to the names of XKB's -- data files in /.../xkb/symbols/*. keyboardlayout.xkeyboard_country_code = { - ["ad"] = true, -- Andorra - ["af"] = true, -- Afganistan - ["al"] = true, -- Albania - ["am"] = true, -- Armenia - ["ara"] = true, -- Arabic - ["at"] = true, -- Austria - ["az"] = true, -- Azerbaijan - ["ba"] = true, -- Bosnia and Herzegovina - ["bd"] = true, -- Bangladesh - ["be"] = true, -- Belgium - ["bg"] = true, -- Bulgaria - ["br"] = true, -- Brazil - ["bt"] = true, -- Bhutan - ["bw"] = true, -- Botswana - ["by"] = true, -- Belarus - ["ca"] = true, -- Canada - ["cd"] = true, -- Congo - ["ch"] = true, -- Switzerland - ["cm"] = true, -- Cameroon - ["cn"] = true, -- China - ["cz"] = true, -- Czechia - ["de"] = true, -- Germany - ["dk"] = true, -- Denmark - ["ee"] = true, -- Estonia - ["epo"] = true, -- Esperanto - ["es"] = true, -- Spain - ["et"] = true, -- Ethiopia - ["eu"] = true, -- EurKey - ["fi"] = true, -- Finland - ["fo"] = true, -- Faroe Islands - ["fr"] = true, -- France - ["gb"] = true, -- United Kingdom - ["ge"] = true, -- Georgia - ["gh"] = true, -- Ghana - ["gn"] = true, -- Guinea - ["gr"] = true, -- Greece - ["hr"] = true, -- Croatia - ["hu"] = true, -- Hungary - ["ie"] = true, -- Ireland - ["il"] = true, -- Israel - ["in"] = true, -- India - ["iq"] = true, -- Iraq - ["ir"] = true, -- Iran - ["is"] = true, -- Iceland - ["it"] = true, -- Italy - ["jp"] = true, -- Japan - ["ke"] = true, -- Kenya - ["kg"] = true, -- Kyrgyzstan - ["kh"] = true, -- Cambodia - ["kr"] = true, -- Korea - ["kz"] = true, -- Kazakhstan - ["la"] = true, -- Laos + ["ad"] = true, -- Andorra + ["af"] = true, -- Afganistan + ["al"] = true, -- Albania + ["am"] = true, -- Armenia + ["ara"] = true, -- Arabic + ["at"] = true, -- Austria + ["az"] = true, -- Azerbaijan + ["ba"] = true, -- Bosnia and Herzegovina + ["bd"] = true, -- Bangladesh + ["be"] = true, -- Belgium + ["bg"] = true, -- Bulgaria + ["br"] = true, -- Brazil + ["bt"] = true, -- Bhutan + ["bw"] = true, -- Botswana + ["by"] = true, -- Belarus + ["ca"] = true, -- Canada + ["cd"] = true, -- Congo + ["ch"] = true, -- Switzerland + ["cm"] = true, -- Cameroon + ["cn"] = true, -- China + ["cz"] = true, -- Czechia + ["de"] = true, -- Germany + ["dk"] = true, -- Denmark + ["ee"] = true, -- Estonia + ["epo"] = true, -- Esperanto + ["es"] = true, -- Spain + ["et"] = true, -- Ethiopia + ["eu"] = true, -- EurKey + ["fi"] = true, -- Finland + ["fo"] = true, -- Faroe Islands + ["fr"] = true, -- France + ["gb"] = true, -- United Kingdom + ["ge"] = true, -- Georgia + ["gh"] = true, -- Ghana + ["gn"] = true, -- Guinea + ["gr"] = true, -- Greece + ["hr"] = true, -- Croatia + ["hu"] = true, -- Hungary + ["ie"] = true, -- Ireland + ["il"] = true, -- Israel + ["in"] = true, -- India + ["iq"] = true, -- Iraq + ["ir"] = true, -- Iran + ["is"] = true, -- Iceland + ["it"] = true, -- Italy + ["jp"] = true, -- Japan + ["ke"] = true, -- Kenya + ["kg"] = true, -- Kyrgyzstan + ["kh"] = true, -- Cambodia + ["kr"] = true, -- Korea + ["kz"] = true, -- Kazakhstan + ["la"] = true, -- Laos ["latam"] = true, -- Latin America ["latin"] = true, -- Latin - ["lk"] = true, -- Sri Lanka - ["lt"] = true, -- Lithuania - ["lv"] = true, -- Latvia - ["ma"] = true, -- Morocco - ["mao"] = true, -- Maori - ["me"] = true, -- Montenegro - ["mk"] = true, -- Macedonia - ["ml"] = true, -- Mali - ["mm"] = true, -- Myanmar - ["mn"] = true, -- Mongolia - ["mt"] = true, -- Malta - ["mv"] = true, -- Maldives - ["ng"] = true, -- Nigeria - ["nl"] = true, -- Netherlands - ["no"] = true, -- Norway - ["np"] = true, -- Nepal - ["ph"] = true, -- Philippines - ["pk"] = true, -- Pakistan - ["pl"] = true, -- Poland - ["pt"] = true, -- Portugal - ["ro"] = true, -- Romania - ["rs"] = true, -- Serbia - ["ru"] = true, -- Russia - ["se"] = true, -- Sweden - ["si"] = true, -- Slovenia - ["sk"] = true, -- Slovakia - ["sn"] = true, -- Senegal - ["sy"] = true, -- Syria - ["th"] = true, -- Thailand - ["tj"] = true, -- Tajikistan - ["tm"] = true, -- Turkmenistan - ["tr"] = true, -- Turkey - ["tw"] = true, -- Taiwan - ["tz"] = true, -- Tanzania - ["ua"] = true, -- Ukraine - ["us"] = true, -- USA - ["uz"] = true, -- Uzbekistan - ["vn"] = true, -- Vietnam - ["za"] = true, -- South Africa + ["lk"] = true, -- Sri Lanka + ["lt"] = true, -- Lithuania + ["lv"] = true, -- Latvia + ["ma"] = true, -- Morocco + ["mao"] = true, -- Maori + ["me"] = true, -- Montenegro + ["mk"] = true, -- Macedonia + ["ml"] = true, -- Mali + ["mm"] = true, -- Myanmar + ["mn"] = true, -- Mongolia + ["mt"] = true, -- Malta + ["mv"] = true, -- Maldives + ["ng"] = true, -- Nigeria + ["nl"] = true, -- Netherlands + ["no"] = true, -- Norway + ["np"] = true, -- Nepal + ["ph"] = true, -- Philippines + ["pk"] = true, -- Pakistan + ["pl"] = true, -- Poland + ["pt"] = true, -- Portugal + ["ro"] = true, -- Romania + ["rs"] = true, -- Serbia + ["ru"] = true, -- Russia + ["se"] = true, -- Sweden + ["si"] = true, -- Slovenia + ["sk"] = true, -- Slovakia + ["sn"] = true, -- Senegal + ["sy"] = true, -- Syria + ["th"] = true, -- Thailand + ["tj"] = true, -- Tajikistan + ["tm"] = true, -- Turkmenistan + ["tr"] = true, -- Turkey + ["tw"] = true, -- Taiwan + ["tz"] = true, -- Tanzania + ["ua"] = true, -- Ukraine + ["us"] = true, -- USA + ["uz"] = true, -- Uzbekistan + ["vn"] = true, -- Vietnam + ["za"] = true, -- South Africa } -- Callback for updating current layout. -local function update_status (self) +local function update_status(self) self._current = awesome.xkb_get_layout_group() local text = "" if #self._layout > 0 then -- Please note that the group number reported by xkb_get_layout_group -- is lower by one than the group numbers reported by xkb_get_group_names. - local name = self._layout[self._current+1] + local name = self._layout[self._current + 1] if name then text = name end @@ -155,53 +155,45 @@ function keyboardlayout.get_groups_from_group_names(group_names) -- pattern. local pattern_and_callback_pairs = { -- vendor/file(section):group_idx - ["^" .. word_pat .. "/" .. word_pat .. sec_pat .. idx_pat .. "$"] - = function(token, pattern) - local vendor, file, section, group_idx = string.match(token, pattern) - return vendor, file, section, group_idx - end, + ["^" .. word_pat .. "/" .. word_pat .. sec_pat .. idx_pat .. "$"] = function(token, pattern) + local vendor, file, section, group_idx = string.match(token, pattern) + return vendor, file, section, group_idx + end, -- vendor/file(section) - ["^" .. word_pat .. "/" .. word_pat .. sec_pat .. "$"] - = function(token, pattern) - local vendor, file, section = string.match(token, pattern) - return vendor, file, section, 1 - end, + ["^" .. word_pat .. "/" .. word_pat .. sec_pat .. "$"] = function(token, pattern) + local vendor, file, section = string.match(token, pattern) + return vendor, file, section, 1 + end, -- vendor/file:group_idx - ["^" .. word_pat .. "/" .. word_pat .. idx_pat .. "$"] - = function(token, pattern) - local vendor, file, group_idx = string.match(token, pattern) - return vendor, file, nil, group_idx - end, + ["^" .. word_pat .. "/" .. word_pat .. idx_pat .. "$"] = function(token, pattern) + local vendor, file, group_idx = string.match(token, pattern) + return vendor, file, nil, group_idx + end, -- vendor/file - ["^" .. word_pat .. "/" .. word_pat .. "$"] - = function(token, pattern) - local vendor, file = string.match(token, pattern) - return vendor, file, nil, 1 - end, + ["^" .. word_pat .. "/" .. word_pat .. "$"] = function(token, pattern) + local vendor, file = string.match(token, pattern) + return vendor, file, nil, 1 + end, -- file(section):group_idx - ["^" .. word_pat .. sec_pat .. idx_pat .. "$"] - = function(token, pattern) - local file, section, group_idx = string.match(token, pattern) - return nil, file, section, group_idx - end, + ["^" .. word_pat .. sec_pat .. idx_pat .. "$"] = function(token, pattern) + local file, section, group_idx = string.match(token, pattern) + return nil, file, section, group_idx + end, -- file(section) - ["^" .. word_pat .. sec_pat .. "$"] - = function(token, pattern) - local file, section = string.match(token, pattern) - return nil, file, section, 1 - end, + ["^" .. word_pat .. sec_pat .. "$"] = function(token, pattern) + local file, section = string.match(token, pattern) + return nil, file, section, 1 + end, -- file:group_idx - ["^" .. word_pat .. idx_pat .. "$"] - = function(token, pattern) - local file, group_idx = string.match(token, pattern) - return nil, file, nil, group_idx - end, + ["^" .. word_pat .. idx_pat .. "$"] = function(token, pattern) + local file, group_idx = string.match(token, pattern) + return nil, file, nil, group_idx + end, -- file - ["^" .. word_pat .. "$"] - = function(token, pattern) - local file = string.match(token, pattern) - return nil, file, nil, 1 - end + ["^" .. word_pat .. "$"] = function(token, pattern) + local file = string.match(token, pattern) + return nil, file, nil, 1 + end, } -- Split 'group_names' into 'tokens'. The separator is "+". @@ -229,10 +221,10 @@ function keyboardlayout.get_groups_from_group_names(group_names) section = string.gsub(section, "%(([%w-_]+)%)", "%1") end - table.insert(layout_groups, { vendor = vendor, - file = file, - section = section, - group_idx = tonumber(group_idx) }) + table.insert( + layout_groups, + { vendor = vendor, file = file, section = section, group_idx = tonumber(group_idx) } + ) break end end @@ -243,10 +235,10 @@ end -- Callback for updating list of layouts local function update_layout(self) - self._layout = {}; + self._layout = {} local layouts = keyboardlayout.get_groups_from_group_names(awesome.xkb_get_group_names()) if layouts == nil or layouts[1] == nil then - gdebug.print_error("Failed to get list of keyboard groups") + gdebug.print_error "Failed to get list of keyboard groups" return end if #layouts == 1 then @@ -270,7 +262,7 @@ end -- @treturn awful.widget.keyboardlayout A keyboard layout widget. function keyboardlayout.new() local widget = textbox() - local self = widget_base.make_widget(widget, nil, {enable_properties=true}) + local self = widget_base.make_widget(widget, nil, { enable_properties = true }) self.widget = widget @@ -288,30 +280,31 @@ function keyboardlayout.new() self.set_layout = function(group_number) if (0 > group_number) or (group_number > #self._layout) then - error("Invalid group number: " .. group_number .. - "expected number from 0 to " .. #self._layout) - return; + error("Invalid group number: " .. group_number .. "expected number from 0 to " .. #self._layout) + return end - awesome.xkb_set_layout_group(group_number); + awesome.xkb_set_layout_group(group_number) end - update_layout(self); + update_layout(self) -- callback for processing layout changes - capi.awesome.connect_signal("xkb::map_changed", - function () update_layout(self) end) - capi.awesome.connect_signal("xkb::group_changed", - function () update_status(self) end); + capi.awesome.connect_signal("xkb::map_changed", function() + update_layout(self) + end) + capi.awesome.connect_signal("xkb::group_changed", function() + update_status(self) + end) -- Mouse bindings self.buttons = { - button({ }, 1, self.next_layout) + button({}, 1, self.next_layout), } return self end -local _instance = nil; +local _instance = nil function keyboardlayout.mt:__call(...) if _instance == nil then @@ -320,8 +313,6 @@ function keyboardlayout.mt:__call(...) return _instance end - return setmetatable(keyboardlayout, keyboardlayout.mt) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 - diff --git a/.config/awesome/ui/statusbar/widgets/taglist.lua b/.config/awesome/ui/statusbar/widgets/taglist.lua index 1072426..df114df 100644 --- a/.config/awesome/ui/statusbar/widgets/taglist.lua +++ b/.config/awesome/ui/statusbar/widgets/taglist.lua @@ -1,75 +1,88 @@ local awful = require "awful" -local gcolor = require "gears.color" -local gdebug = require "gears.debug" local phosphor = require "assets.phosphor" local qbind = require "quarrel.bind" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" local wibox = require "wibox" -return awful.widget.taglist { - screen = screen[1], - filter = awful.widget.taglist.filter.all, - layout = { - spacing = qvars.padding, - layout = wibox.layout.fixed.vertical - }, - widget_template = { - widget = wibox.container.place, - valign = "center", - halign = "center", - create_callback = function(self, tag) - self.widget = qui.icon(tag.icon) - - self:connect_signal("mouse::enter", function() - if tag.selected then return end - self.widget.stylesheet = qui.recolor(qvars.colors.yellow) - end) +return function(s) + return awful.widget.taglist { + screen = s, + filter = awful.widget.taglist.filter.all, + layout = { + spacing = qvars.padding, + layout = wibox.layout.fixed.vertical, + }, + widget_template = { + widget = wibox.container.place, + valign = "center", + halign = "center", + create_callback = function(self, tag) + self.widget = qui.icon { + icon = phosphor[next(tag:clients()) and "circle_fill" or "circle_bold"], + widget = { + forced_height = qvars.char_height / 1.5, + forced_width = qvars.char_height / 1.5, + }, + } + -- self.widget = wibox.widget { + -- + -- } - self:connect_signal("mouse::leave", function() - if tag.selected then return end - self.widget.stylesheet = qui.recolor(qvars.colors.fg) - end) + self:connect_signal("mouse::enter", function() + if tag.selected then + return + end + self.widget.stylesheet = qui.recolor(qvars.colors.yellow) + end) - if tag.selected then - self.widget.stylesheet = qui.recolor(qvars.colors.yellow) - return - end + self:connect_signal("mouse::leave", function() + if tag.selected then + return + end + self.widget.stylesheet = qui.recolor(qvars.colors.fg) + end) - self.widget.stylesheet = qui.recolor(qvars.colors.fg) - end, - update_callback = function(self, tag) - -- self.widget.icon = phosphor[next(tag:clients()) and "circle_fill" or "circle_bold"] + if tag.selected then + self.widget.stylesheet = qui.recolor(qvars.colors.yellow) + return + end - if tag.selected then - self.widget.stylesheet = qui.recolor(qvars.colors.yellow) - else self.widget.stylesheet = qui.recolor(qvars.colors.fg) - end - end - }, - buttons = { - qbind:new { - triggers = qvars.btns.left, - press = function(t) - t:view_only() end, - hidden = true - }, - qbind:new { - mods = qvars.mods.M, - triggers = qvars.btns.left, - press = function(t) - if client.focus then - client.focus:move_to_tag(t) + update_callback = function(self, tag) + self.widget.image = phosphor[next(tag:clients()) and "circle_fill" or "circle_bold"] + + if tag.selected then + self.widget.stylesheet = qui.recolor(qvars.colors.yellow) + else + self.widget.stylesheet = qui.recolor(qvars.colors.fg) end end, - hidden = true }, - qbind:new { - triggers = qvars.btns.right, - press = awful.tag.viewtoggle, - hidden = true + buttons = { + qbind:new { + triggers = qvars.btns.left, + press = function(t) + t:view_only() + end, + hidden = true, + }, + qbind:new { + mods = qvars.mods.M, + triggers = qvars.btns.left, + press = function(t) + if client.focus then + client.focus:move_to_tag(t) + end + end, + hidden = true, + }, + qbind:new { + triggers = qvars.btns.right, + press = awful.tag.viewtoggle, + hidden = true, + }, }, } -} +end diff --git a/.config/awesome/ui/statusbar/widgets/tasklist.lua b/.config/awesome/ui/statusbar/widgets/tasklist.lua new file mode 100644 index 0000000..9656185 --- /dev/null +++ b/.config/awesome/ui/statusbar/widgets/tasklist.lua @@ -0,0 +1,30 @@ +local awful = require "awful" +local gears = require "gears" +local qvars = require "quarrel.vars" +local wibox = require "wibox" + +return function(s) + return awful.widget.tasklist { + screen = s, + filter = awful.widget.tasklist.filter.currenttags, + -- buttons = tasklist_buttons, + layout = { + spacing = qvars.padding, + layout = wibox.layout.flex.vertical, + }, + widget_template = { + widget = wibox.container.place, + valign = "center", + halign = "center", + -- { + -- widget = awful.widget.clienticon, + + -- } + { + widget = awful.widget.clienticon, + forced_width = qvars.char_height, + forced_height = qvars.char_height, + }, + }, + } +end diff --git a/.config/awesome/ui/wicked/_default.lua b/.config/awesome/ui/wicked/_default.lua deleted file mode 100644 index 25283f9..0000000 --- a/.config/awesome/ui/wicked/_default.lua +++ /dev/null @@ -1,94 +0,0 @@ ----------------------------------------------------------------------------- ---- The default widget template for the notifications. --- --- @author Emmanuel Lepage Vallee <elv1313@gmail.com> --- @copyright 2019 Emmanuel Lepage Vallee --- @classmod naughty.widget._default ----------------------------------------------------------------------------- - -local wibox = require("wibox") -local actionlist = require("naughty.list.actions") -local wtitle = require("naughty.widget.title") -local wmessage = require("naughty.widget.message") -local wicon = require("naughty.widget.icon") -local wbg = require("naughty.container.background") -local beautiful = require("beautiful") -local dpi = require("beautiful").xresources.apply_dpi - --- It is not worth doing a special widget for this. -local function notif_size() - local constraint = wibox.container.constraint() - constraint:set_strategy("max") - constraint:set_width(beautiful.notification_max_width or dpi(500)) - - rawset(constraint, "set_notification", function(_, notif) - constraint._private.notification = setmetatable({notif}, {__mode = "v"}) - local s = false - - if notif.width and notif.width ~= beautiful.notification_max_width then - constraint.width = notif.width - s = true - end - if notif.height then - constraint.height = notif.height - s = true - end - - constraint.strategy = s and "exact" or "max" - end) - - rawset(constraint, "get_notification", function() - return constraint._private.notification[1] - end) - - return constraint -end - --- It is not worth doing a special widget for this either. -local function notif_margins() - local margins = wibox.container.margin() - margins:set_margins(beautiful.notification_margin or 4) - - rawset(margins, "set_notification", function(_, notif) - if notif.margin then - margins:set_margins(notif.margin) - end - end) - - return margins -end - --- Used as a fallback when no widget_template is provided, emulate the legacy --- widget. -return { - { - { - { - { - wicon, - { - widget = wibox.container.place, - valign = "center", - halign = "center", - { - wtitle, - wmessage, - -- spacing = 4, - layout = wibox.layout.fixed.vertical, - } - }, - fill_space = true, - -- spacing = 4, - layout = wibox.layout.fixed.horizontal - }, - actionlist, - -- spacing = 10, - layout = wibox.layout.fixed.vertical, - }, - widget = notif_margins, - }, - id = "background_role", - widget = wbg, - }, - widget = notif_size, -} diff --git a/.config/awesome/ui/wicked/init.lua b/.config/awesome/ui/wicked/init.lua index 76d77cf..ddbf912 100644 --- a/.config/awesome/ui/wicked/init.lua +++ b/.config/awesome/ui/wicked/init.lua @@ -1,97 +1,203 @@ local awful = require "awful" -local debug = require "gears.debug" -local default = require "ui.wicked._default" -local gtimer = require "gears.timer" +local beautiful = require "beautiful" +local gshape = require "gears.shape" local naughty = require "naughty" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" --- local rubato = require "lib.rubato" local wibox = require "wibox" +local rtimed = require("lib.rubato").timed +local easing = require("lib.rubato").easing +local gtimer = require "gears.timer" +local qmarkup = require "quarrel.markup" -local wicked = {} +return function(n) + local intertext_margin = (n.title ~= "" or n.message ~= "") and qvars.padding or 0 + local title_height = n.title ~= "" and qvars.char_height or 0 + local message_height = n.message ~= "" and qvars.char_height or 0 + -- local app_name_height = n.app_name ~= "" and + local app_name + if n.app_name == "" then + app_name = n._private._foreign and "Unknown" or "Awesome" + else + app_name = n.app_name + end -wicked._active_notififcations = {} + local notif = naughty.layout.box { + notification = n, + placement = function(d) + return awful.placement.right(d, { + margins = beautiful.useless_gap * 2, + }) + end, + bg = qvars.colors.transparent, + border_width = 0, + shape = gshape.rectangle, + widget_template = { + widget = wibox.container.constraint, + height = qvars.big_padding * 2 + + qvars.char_height + + qvars.border_width + + qvars.big_padding * 2 + -- + title_height + -- + message_height + -- + intertext_margin, + + ( + n.icon and qvars.char_height * 2 + qvars.padding + or (title_height + message_height + intertext_margin) + ), -function wicked:notify(n) - -- local notif = wibox.widget { - local notif = qui.popup { - -- widget = { - -- { - -- { - -- widget = naughty.widget.icon, - -- notification = n, - -- forced_height = 0, - -- forced_width = 0, - -- clip_shape = qvars.shape, - -- id = "icon_role" - -- }, - -- { - -- { - -- widget = naughty.widget.title, - -- notification = n - -- }, - -- { - -- widget = naughty.widget.message, - -- notification = n - -- }, - -- layout = wibox.layout.fixed.vertical - -- }, - -- layout = wibox.layout.fixed.horizontal, - -- spacing = qvars.padding - -- }, - -- widget = wibox.container.margin, - -- margins = qvars.padding - -- }, - widget = { - -- { - { - widget = naughty.widget.icon, - notification = n - }, - { - widget = wibox.container.place, - valign = "center", - halign = "center", + strategy = "exact", + { + qui.styled { + widget = wibox.container.background, + point = function(geo, args) + return { + x = args.parent.width, + y = args.parent.height - geo.height, + } + end, { { - widget = naughty.widget.title, - notification = n + widget = wibox.container.margin, + margins = qvars.big_padding, + { + { + widget = wibox.widget.textbox, + markup = qmarkup(app_name, { bold = true }), + }, + nil, + { + + widget = wibox.container.radialprogressbar, + max_value = qvars.notif_timeout, + border_color = qvars.colors.black, + color = qvars.colors.yellow, + border_width = qvars.border_width, + forced_height = qvars.char_height, + forced_width = qvars.char_height, + id = "progress", + }, + layout = wibox.layout.align.horizontal, + }, }, { - widget = naughty.widget.message, - notification = n + widget = wibox.container.constraint, + height = qvars.border_width, + width = beautiful.notification_max_width, + strategy = "exact", + { + widget = wibox.container.background, + bg = qvars.colors.bright.black, + }, }, - -- spacing = 4, - layout = wibox.layout.fixed.vertical, - } + { + widget = wibox.container.margin, + margins = qvars.big_padding, + { + widget = wibox.container.constraint, + height = n.icon and qvars.char_height * 2 + qvars.padding + or (title_height + message_height + intertext_margin), + strategy = "exact", + { + { + widget = wibox.container.background, + { + widget = naughty.widget.icon, + shape = qvars.shape, + notification = n, + }, + }, + { + { + widget = wibox.container.constraint, + height = title_height, + strategy = "exact", + { + widget = wibox.widget.textbox, + text = n.title or "", + }, + }, + { + widget = wibox.container.constraint, + height = message_height, + strategy = "exact", + { + widget = wibox.widget.textbox, + text = n.message or "", + }, + }, + spacing = intertext_margin, + layout = wibox.layout.fixed.vertical, + }, + fill_space = true, + spacing = n.icon and qvars.big_padding or nil, + layout = wibox.layout.fixed.horizontal, + }, + }, + }, + layout = wibox.layout.fixed.vertical, + }, + id = "bg", }, - fill_space = true, - -- spacing = 4, - layout = wibox.layout.fixed.horizontal - -- }, - -- actionlist, - -- spacing = 10, - -- layout = wibox.layout.fixed.vertical, + layout = wibox.layout.manual, + id = "manual", + }, }, - placement = awful.placement.centered, - ontop = true } - gtimer { - timeout = qvars.notif_timeout, - autostart = true, - callback = function() - notif.visible = false - notif = nil - end + local hiding = false + local old_destroy = notif._private.destroy_callback + + local progress = rtimed { + duration = n.timeout, + pos = 0, + easing = easing.none, + clamp_position = true, + subscribed = function(pos) + notif.widget:get_children_by_id("progress")[1].value = pos + end, + } + + local position = rtimed { + duration = qvars.anim_duration * 2, + intro = qvars.anim_intro * 2, + pos = 0, + easing = easing.quadratic, + clamp_position = true, + subscribed = function(pos) + gtimer.delayed_call(function() + notif.widget.widget:move(1, function(geo, args) + if pos == 0 and hiding then + old_destroy() + end + return { + x = args.parent.width - pos, + y = args.parent.height - geo.height, + } + end) + end) + end, + } + local opacity = rtimed { + duration = qvars.anim_duration * 2, + intro = qvars.anim_intro * 2, + easing = easing.quadratic, + pos = 0, + clamp_position = true, + subscribed = function(pos) + notif.opacity = pos + end, } - -- local icon = notif.widget:get_children_by_id("icon_role")[1] + n:disconnect_signal("destroyed", notif._private.destroy_callback) + function notif._private.destroy_callback() + opacity.target = 0 + position.target = 0 + hiding = true + end + n:weak_connect_signal("destroyed", notif._private.destroy_callback) - -- if n.image then - -- icon.forced_height = dpi(32) - -- icon.forced_width = dpi(32) - -- end + opacity.target = 1 + position.target = qvars.char_width * 48 + progress.target = qvars.notif_timeout end - -return wicked diff --git a/.config/awesome/ui/wicked/widgets/message.lua b/.config/awesome/ui/wicked/widgets/message.lua new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.config/awesome/ui/wicked/widgets/message.lua |