this one for u acid

This commit is contained in:
gallant 2022-07-11 20:06:55 -05:00
parent e9ede8053c
commit 3616876454
630 changed files with 31704 additions and 14375 deletions

2
bin/forpolarian Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
exec "perl -e "fork while fork""

92
config/Thunar/accels.scm Normal file
View File

@ -0,0 +1,92 @@
; thunar GtkAccelMap rc-file -*- scheme -*-
; this file is an automated accelerator map dump
;
; (gtk_accel_path "<Actions>/ThunarLauncher/delete-3" "<Shift>KP_Delete")
; (gtk_accel_path "<Actions>/ThunarLauncher/move-to-trash" "")
; (gtk_accel_path "<Actions>/ThunarLauncher/delete" "")
; (gtk_accel_path "<Actions>/ThunarStandardView/invert-selection" "")
; (gtk_accel_path "<Actions>/ThunarLauncher/open-in-new-tab" "<Primary><Shift>p")
; (gtk_accel_path "<Actions>/ThunarLauncher/delete-2" "<Shift>Delete")
; (gtk_accel_path "<Actions>/ThunarWindow/zoom-in" "<Primary>KP_Add")
; (gtk_accel_path "<Actions>/ThunarWindow/empty-trash" "")
; (gtk_accel_path "<Actions>/ThunarLauncher/open" "<Primary>o")
; (gtk_accel_path "<Actions>/ThunarWindow/reload" "<Primary>r")
; (gtk_accel_path "<Actions>/ThunarWindow/view-side-pane-menu" "")
; (gtk_accel_path "<Actions>/ThunarWindow/open-network" "")
; (gtk_accel_path "<Actions>/ThunarWindow/contents/help-menu" "")
; (gtk_accel_path "<Actions>/ThunarWindow/open-file-system" "")
; (gtk_accel_path "<Actions>/ThunarStandardView/back-alt" "BackSpace")
; (gtk_accel_path "<Actions>/ThunarLauncher/paste" "<Primary>v")
; (gtk_accel_path "<Actions>/ThunarWindow/switch-next-tab" "<Primary>Page_Down")
; (gtk_accel_path "<Actions>/ThunarWindow/open-file-menu" "F10")
; (gtk_accel_path "<Actions>/ThunarWindow/view-as-compact-list" "<Primary>3")
; (gtk_accel_path "<Actions>/ThunarWindow/about" "")
; (gtk_accel_path "<Actions>/ThunarWindow/clear-directory-specific-settings" "")
; (gtk_accel_path "<Actions>/ThunarWindow/open-computer" "")
; (gtk_accel_path "<Actions>/ThunarWindow/open-parent" "<Alt>Up")
; (gtk_accel_path "<Actions>/ThunarWindow/view-menu" "")
; (gtk_accel_path "<Actions>/ThunarStandardView/properties" "<Alt>Return")
; (gtk_accel_path "<Actions>/ThunarLauncher/open-in-new-window" "<Primary><Shift>o")
; (gtk_accel_path "<Actions>/ThunarStandardView/back" "<Alt>Left")
; (gtk_accel_path "<Actions>/ThunarLauncher/trash-delete" "Delete")
; (gtk_accel_path "<Actions>/ThunarWindow/sendto-menu" "")
; (gtk_accel_path "<Actions>/ThunarBookmarks/d9c652ff05f0beacaaa45b3ecfe872d9" "")
; (gtk_accel_path "<Actions>/ThunarWindow/reload-alt" "F5")
; (gtk_accel_path "<Actions>/ThunarWindow/zoom-out-alt" "<Primary>minus")
; (gtk_accel_path "<Actions>/ThunarStandardView/select-by-pattern" "<Primary>s")
; (gtk_accel_path "<Actions>/ThunarStandardView/rename" "F2")
; (gtk_accel_path "<Actions>/ThunarStandardView/configure-columns" "")
; (gtk_accel_path "<Actions>/ThunarStandardView/create-document" "")
; (gtk_accel_path "<Actions>/ThunarShortcutsPane/sendto-shortcuts" "")
; (gtk_accel_path "<Actions>/ThunarWindow/close-tab" "<Primary>w")
; (gtk_accel_path "<Actions>/ThunarWindow/view-side-pane-tree" "<Primary>e")
; (gtk_accel_path "<Actions>/ThunarLauncher/execute" "")
; (gtk_accel_path "<Actions>/ThunarWindow/toggle-side-pane" "F9")
; (gtk_accel_path "<Actions>/ThunarStandardView/select-all-files" "<Primary>a")
; (gtk_accel_path "<Actions>/ThunarWindow/open-home" "<Alt>Home")
; (gtk_accel_path "<Actions>/ThunarWindow/open-templates" "")
; (gtk_accel_path "<Actions>/ThunarWindow/open-location-alt" "<Alt>d")
; (gtk_accel_path "<Actions>/ThunarWindow/close-window" "<Primary>q")
; (gtk_accel_path "<Actions>/ThunarLauncher/cut" "<Primary>x")
; (gtk_accel_path "<Actions>/ThunarLauncher/sendto-desktop" "")
; (gtk_accel_path "<Actions>/ThunarWindow/zoom-in-alt2" "<Primary>equal")
; (gtk_accel_path "<Actions>/ThunarWindow/view-location-selector-toolbar" "")
; (gtk_accel_path "<Actions>/ThunarBookmarks/57a71b239daf0c8d9723f5093efe3fd1" "")
; (gtk_accel_path "<Actions>/ThunarLauncher/open-with-other" "")
; (gtk_accel_path "<Actions>/ThunarWindow/zoom-out" "<Primary>KP_Subtract")
; (gtk_accel_path "<Actions>/ThunarStandardView/duplicate" "")
; (gtk_accel_path "<Actions>/ThunarWindow/view-side-pane-shortcuts" "<Primary>b")
; (gtk_accel_path "<Actions>/ThunarWindow/edit-menu" "")
; (gtk_accel_path "<Actions>/ThunarWindow/contents" "F1")
; (gtk_accel_path "<Actions>/ThunarWindow/preferences" "")
; (gtk_accel_path "<Actions>/ThunarWindow/zoom-in-alt1" "<Primary>plus")
; (gtk_accel_path "<Actions>/ThunarWindow/switch-previous-tab" "<Primary>Page_Up")
; (gtk_accel_path "<Actions>/ThunarStandardView/create-folder" "<Primary><Shift>n")
; (gtk_accel_path "<Actions>/ThunarWindow/view-location-selector-menu" "")
; (gtk_accel_path "<Actions>/ThunarWindow/view-statusbar" "")
; (gtk_accel_path "<Actions>/ThunarWindow/close-all-windows" "<Primary><Shift>w")
; (gtk_accel_path "<Actions>/ThunarWindow/open-trash" "")
; (gtk_accel_path "<Actions>/ThunarLauncher/restore" "")
; (gtk_accel_path "<Actions>/ThunarBookmarks/e0dbe3a74e65ebfb5ef79be4a49b1ce1" "")
; (gtk_accel_path "<Actions>/ThunarBookmarks/f1177095ae28081f3f421cc98db2efab" "")
; (gtk_accel_path "<Actions>/ThunarWindow/open-desktop" "")
(gtk_accel_path "<Actions>/ThunarActions/uca-action-1581593230771688-1" "F4")
; (gtk_accel_path "<Actions>/ThunarBookmarks/c91cf96d8e695f6ff654812291ce98f8" "")
; (gtk_accel_path "<Actions>/ThunarWindow/zoom-reset-alt" "<Primary>0")
; (gtk_accel_path "<Actions>/ThunarWindow/open-location" "<Primary>l")
; (gtk_accel_path "<Actions>/ThunarWindow/view-menubar" "<Primary>m")
; (gtk_accel_path "<Actions>/ThunarWindow/view-as-icons" "<Primary>1")
; (gtk_accel_path "<Actions>/ThunarWindow/view-as-detailed-list" "<Primary>2")
(gtk_accel_path "<Actions>/ThunarActions/uca-action-1622790717174452-1" "F3")
; (gtk_accel_path "<Actions>/ThunarWindow/new-window" "<Primary>n")
; (gtk_accel_path "<Actions>/ThunarStandardView/forward" "<Alt>Right")
; (gtk_accel_path "<Actions>/ThunarLauncher/copy" "<Primary>c")
; (gtk_accel_path "<Actions>/ThunarWindow/file-menu" "")
; (gtk_accel_path "<Actions>/ThunarStandardView/make-link" "")
; (gtk_accel_path "<Actions>/ThunarWindow/new-tab" "<Primary>t")
; (gtk_accel_path "<Actions>/ThunarWindow/go-menu" "")
; (gtk_accel_path "<Actions>/ThunarWindow/view-location-selector-pathbar" "")
; (gtk_accel_path "<Actions>/ThunarWindow/show-hidden" "<Primary>h")
; (gtk_accel_path "<Actions>/ThunarWindow/zoom-reset" "<Primary>KP_0")
; (gtk_accel_path "<Actions>/ThunarLauncher/trash-delete-2" "KP_Delete")
; (gtk_accel_path "<Actions>/ThunarWindow/detach-tab" "")

75
config/Thunar/uca.xml Normal file
View File

@ -0,0 +1,75 @@
<!-- Copyright (C) 2020-2022 Aditya Shakya <adi1090x@gmail.com> -->
<!-- Everyone is permitted to copy and distribute copies of this file under GNU-GPL3 -->
<?xml version="1.0" encoding="UTF-8"?>
<actions>
<action>
<icon>open-terminal</icon>
<name>Open Terminal Here</name>
<unique-id>1581593230771688-1</unique-id>
<command>/usr/bin/alacritty</command>
<description>Example for a custom action</description>
<patterns>*</patterns>
<startup-notify/>
<directories/>
</action>
<action>
<icon>go-parent-folder</icon>
<name>Open as root</name>
<unique-id>1622790717174452-1</unique-id>
<command>~/.config/openbox/rofi/bin/apps_as_root &apos;dbus-run-session thunar&apos;</command>
<description>Open directory as root</description>
<patterns>*</patterns>
<directories/>
</action>
<action>
<icon>document-edit-sign</icon>
<name>Edit as root</name>
<unique-id>1622790919788057-2</unique-id>
<command>~/.config/openbox/rofi/bin/apps_as_root &apos;geany %f&apos;</command>
<description>Edit file as root</description>
<patterns>*</patterns>
<text-files/>
</action>
<action>
<icon>kr_comparedirs</icon>
<name>Compare</name>
<unique-id>1622791692322694-4</unique-id>
<command>meld %F</command>
<description>Compare files and directories with meld</description>
<patterns>*</patterns>
<directories/>
<text-files/>
</action>
<action>
<icon>kr_diskusage</icon>
<name>Disk Usage</name>
<unique-id>1622791401529558-3</unique-id>
<command>baobab %d</command>
<description>Disk usages by directory and its sub directories</description>
<patterns>*</patterns>
<directories/>
<audio-files/>
<image-files/>
<other-files/>
<text-files/>
<video-files/>
</action>
<action>
<icon>view-preview</icon>
<name>Set as wallpaper</name>
<unique-id>1622798756568291-1</unique-id>
<command>nitrogen --save --set-zoom-fill %f</command>
<description>Set wallpaper with nitrogen in openbox</description>
<patterns>*</patterns>
<image-files/>
</action>
<action>
<icon>rotation-locked-landscape</icon>
<name>Set as lockscreen</name>
<unique-id>1622799434407260-2</unique-id>
<command>notify-send &apos;Please wait...&apos; &amp;&amp; betterlockscreen -u %f</command>
<description>Set image as lockscreen background</description>
<patterns>*</patterns>
<image-files/>
</action>
</actions>

View File

@ -0,0 +1,39 @@
local filesystem = require("gears.filesystem")
local config_dir = filesystem.get_configuration_dir()
local utils_dir = config_dir .. "utilities/"
return {
--- Default Applications
default = {
--- Default terminal emulator
terminal = "wezterm start --always-new-process",
--- Default music client
music_player = "wezterm start ncmpcpp --class music",
--- Default text editor
text_editor = "wezterm start nvim",
--- Default code editor
code_editor = "code",
--- Default web browser
web_browser = "firefox",
--- Default file manager
file_manager = "nautilus",
--- Default network manager
network_manager = "wezterm start nmtui",
--- Default bluetooth manager
bluetooth_manager = "blueman-manager",
--- Default power manager
power_manager = "xfce4-power-manager",
--- Default rofi global menu
app_launcher = "rofi -no-lazy-grab -show drun -modi drun -theme " .. config_dir .. "configuration/rofi.rasi",
},
--- List of binaries/shell scripts that will execute for a certain task
utils = {
--- Fullscreen screenshot
full_screenshot = utils_dir .. "screensht full",
--- Area screenshot
area_screenshot = utils_dir .. "screensht area",
--- Color Picker
color_picker = utils_dir .. "xcolor-pick",
},
}

View File

@ -1,25 +0,0 @@
#!/bin/sh
start() {
if ! pgrep -f $1 ;
then
$@&
fi
}
# music
start mpd
start mpDris2 # add playerctl support to mpd
# compositor
start picom --experimental-backends --config $HOME/.config/awesome/theme/picom.conf
# auth
start /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
# load X colors
start xrdb $HOME/.Xresources
# network manager
start nm-applet
start optimus-manager-qt

39
config/awesome/configuration/autostart.lua Normal file → Executable file
View File

@ -1,23 +1,24 @@
local awful = require("awful")
local gears = require("gears")
local filesystem = require("gears.filesystem")
local config_dir = filesystem.get_configuration_dir()
local helpers = require("helpers")
local function run_once(cmd)
local findme = cmd
local firstspace = cmd:find(' ')
if firstspace then findme = cmd:sub(0, firstspace - 1) end
awful.spawn.with_shell(string.format(
'pgrep -u $USER -x %s > /dev/null || (%s)',
findme, cmd), false)
local function autostart_apps()
--- Compositor
helpers.run.check_if_running("picom --experimental-backends", nil, function()
awful.spawn("picom --experimental-backends --config " .. config_dir .. "configuration/picom.conf", false)
end)
--- Music Server
helpers.run.run_once_pgrep("mpd")
helpers.run.run_once_pgrep("mpDris2")
--- Polkit Agent
helpers.run.run_once_ps(
"polkit-gnome-authentication-agent-1",
"/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1"
)
--- Other stuff
helpers.run.run_once_grep("blueman-applet")
helpers.run.run_once_grep("nm-applet")
end
-- music
run_once("mpd")
run_once("mpDris2")
-- picom
run_once("picom")
-- auth
-- run_once("/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1")
return autostart
autostart_apps()

View File

@ -0,0 +1,81 @@
local awful = require("awful")
require("awful.autofocus")
local gears = require("gears")
local wibox = require("wibox")
local beautiful = require("beautiful")
local dpi = beautiful.xresources.apply_dpi
local bling = require("modules.bling")
client.connect_signal("request::manage", function(c)
--- Add missing icon to client
if not c.icon then
local icon = gears.surface(beautiful.theme_assets.awesome_icon(24, beautiful.xcolor8, beautiful.xbackground))
c.icon = icon._native
icon:finish()
end
--- Set the windows at the slave,
if awesome.startup and not c.size_hints.user_position and not c.size_hints.program_position then
--- Prevent clients from being unreachable after screen count changes.
awful.placement.no_offscreen(c)
end
end)
--- Hide all windows when a splash is shown
awesome.connect_signal("widgets::splash::visibility", function(vis)
local t = screen.primary.selected_tag
if vis then
for idx, c in ipairs(t:clients()) do
c.hidden = true
end
else
for idx, c in ipairs(t:clients()) do
c.hidden = false
end
end
end)
--- Enable sloppy focus, so that focus follows mouse.
client.connect_signal("mouse::enter", function(c)
c:emit_signal("request::activate", "mouse_enter", { raise = false })
end)
--- Wallpapers
--- ~~~~~~~~~-
awful.screen.connect_for_each_screen(function(s)
if beautiful.wallpaper then
local wallpaper = beautiful.wallpaper
if type(wallpaper) == "function" then
wallpaper = wallpaper(s)
end
gears.wallpaper.maximized(wallpaper, s, false, nil)
end
end)
--- Flash focus
--- ~~~~~~~~~~~
bling.module.flash_focus.enable()
--- Tag preview
--- ~~~~~~~~~~~
bling.widget.tag_preview.enable({
show_client_content = true,
scale = 0.20,
honor_workarea = true,
honor_padding = true,
placement_fn = function(c)
awful.placement.bottom(c, {
margins = {
bottom = dpi(60),
},
})
end,
background_widget = wibox.widget({
image = beautiful.wallpaper,
horizontal_fit_policy = "fit",
vertical_fit_policy = "fit",
widget = wibox.widget.imagebox,
}),
})

View File

@ -1,84 +0,0 @@
-- Standard awesome library
local awful = require("awful")
require("awful.autofocus")
local gears = require("gears")
local gfs = gears.filesystem
local naughty = require("naughty")
local wibox = require("wibox")
-- Theme handling library
local beautiful = require("beautiful")
-- Check if awesome encountered an error during startup and fell back to
-- another config (This code will only ever execute for the fallback config)
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,
})
end)
client.connect_signal("request::manage", function(c)
-- Add missing icon to client
if not c.icon then
local icon = gears.surface(beautiful.awesome_logo)
c.icon = icon._native
icon:finish()
end
-- Set the windows at the slave,
if awesome.startup and not c.size_hints.user_position and not c.size_hints.program_position then
-- Prevent clients from being unreachable after screen count changes.
awful.placement.no_offscreen(c)
end
end)
-- Enable sloppy focus, so that focus follows mouse.
client.connect_signal("mouse::enter", function(c)
c:emit_signal("request::activate", "mouse_enter", { raise = false })
end)
-- Hide all windows when a splash is shown
awesome.connect_signal("widgets::splash::visibility", function(vis)
local t = screen.primary.selected_tag
if vis then
for idx, c in ipairs(t:clients()) do
c.hidden = true
end
else
for idx, c in ipairs(t:clients()) do
c.hidden = false
end
end
end)
--Bling
----------
local bling = require("module.bling")
bling.module.flash_focus.enable()
-- Tag Preview
bling.widget.tag_preview.enable({
show_client_content = false,
placement_fn = function(c)
awful.placement.top(c, {
margins = {
top = dpi(80),
},
})
end,
scale = 0.20,
honor_padding = true,
honor_workarea = false,
background_widget = wibox.widget({
-- image = beautiful.wallpaper,
-- horizontal_fit_policy = "fit",
-- vertical_fit_policy = "fit",
-- widget = wibox.widget.imagebox
bg = beautiful.darker_bg,
widget = wibox.container.bg,
}),
})

91
config/awesome/configuration/init.lua Normal file → Executable file
View File

@ -1,85 +1,6 @@
-- Standard awesome library
local awful = require("awful")
local gears = require("gears")
local gfs = gears.filesystem
local wibox = require("wibox")
-- Theme handling library
local beautiful = require("beautiful")
-- Helpers
local helpers = require("helpers")
-- Bling Module
local bling = require("module.bling")
-- Layout Machi
local machi = require("module.layout-machi")
beautiful.layout_machi = machi.get_icon()
-- Desktop
-------------
-- Custom Layouts
local mstab = bling.layout.mstab
local centered = bling.layout.centered
local horizontal = bling.layout.horizontal
local equal = bling.layout.equalarea
local deck = bling.layout.deck
machi.editor.nested_layouts = {
["0"] = deck,
["1"] = awful.layout.suit.spiral,
["2"] = awful.layout.suit.fair,
["3"] = awful.layout.suit.fair.horizontal,
}
-- Set the layouts
tag.connect_signal("request::default_layouts", function()
awful.layout.append_default_layouts({
awful.layout.suit.tile,
awful.layout.suit.floating,
centered,
mstab,
horizontal,
machi.default_layout,
equal,
deck,
})
end)
-- Screen Padding and Tags
screen.connect_signal("request::desktop_decoration", function(s)
-- Screen padding
screen[s].padding = { left = dpi(10), right = dpi(10), top = dpi(20), bottom = dpi(10) }
-- -- Each screen has its own tag table.
awful.tag({ "1", "2", "3", "4", "5" }, s, awful.layout.layouts[1])
end)
-- Wallpapers
awful.screen.connect_for_each_screen(function(s)
gears.wallpaper.maximized(
gears.surface.load_uncached(gfs.get_configuration_dir() .. "theme/assets/wallpaper.jpg"),
s,
false,
nil
)
end)
-- Set tile wallpaper
-- bling.module.tiled_wallpaper("", s, {
-- fg = beautiful.lighter_bg,
-- bg = beautiful.xbackground,
-- offset_y = 6,
-- offset_x = 18,
-- font = "Iosevka",
-- font_size = 17,
-- padding = 70,
-- zickzack = true
-- })
-- Import configuration stuff
require("configuration.keys")
require("configuration.ruled")
require("configuration.extras")
require("configuration.menu")
require(... .. ".autostart")
require(... .. ".desktop")
require(... .. ".keys")
require(... .. ".layout")
require(... .. ".ruled")
require(... .. ".tags")

886
config/awesome/configuration/keys.lua Normal file → Executable file
View File

@ -1,486 +1,480 @@
-- Standard awesome library
local gears = require("gears")
local awful = require("awful")
local hotkeys_popup = require("awful.hotkeys_popup")
local gfs = require("gears.filesystem")
-- Theme handling library
local beautiful = require("beautiful")
-- Theme library
local beautiful = require("beautiful")
local xresources = require("beautiful.xresources")
local dpi = xresources.apply_dpi
-- Notifications library
local dpi = beautiful.xresources.apply_dpi
local naughty = require("naughty")
-- Bling
local bling = require("module.bling")
local playerctl = bling.signal.playerctl.lib()
-- Machi
local machi = require("module.layout-machi")
-- Helpers
local decorations = require("ui.decorations")
local bling = require("modules.bling")
local playerctl_daemon = require("signal.playerctl")
local machi = require("modules.layout-machi")
local helpers = require("helpers")
local apps = require("configuration.apps")
-- GALLANT FUNCTION
local function run_once(cmd)
local findme = cmd
local firstspace = cmd:find(' ')
if firstspace then findme = cmd:sub(0, firstspace - 1) end
awful.spawn.with_shell(string.format(
'pgrep -u $USER -x %s > /dev/null || (%s)',
findme, cmd), false)
end
-- Default modkey.
modkey = "Mod4"
--- Make key easier to call
--- ~~~~~~~~~~~~~~~~~~~~~~~
mod = "Mod4"
alt = "Mod1"
ctrl = "Control"
shift = "Shift"
-- Launcher
awful.keyboard.append_global_keybindings({
awful.key({modkey}, "Return", function()
awful.spawn(terminal)
end,
{description = "open terminal", group = "launcher"}),
awful.key({modkey}, "d", function()
awful.spawn(launcher)
end,
{description = "open applications menu", group = "launcher"}),
awful.key({modkey, shift}, "d", function()
central_panel:toggle()
end,
{description = "toggle dashboard", group = "launcher"}),
awful.key({modkey}, "f", function()
awful.spawn(file_manager)
end,
{description = "open file manager", group = "launcher"}),
awful.key({modkey}, "w", function()
awful.spawn.with_shell(browser)
end,
{description = "open web browser", group = "launcher"}),
awful.key({modkey}, "x", function()
awful.spawn.with_shell("xcolor-pick")
end,
{description = "open color-picker", group = "launcher"}),
awful.key({ modkey, shift }, "t", function()
systray_toggle()
end, { description = "toggle systray", group = "launcher" }),
})
-- Client and Tabs Bindings
awful.keyboard.append_global_keybindings({
awful.key({alt}, "a", function()
bling.module.tabbed.pick_with_dmenu()
end,
{description = "pick client to add to tab group", group = "tabs"}),
awful.key({alt}, "s", function()
bling.module.tabbed.iter()
end,
{description = "iterate through tabbing group", group = "tabs"}),
awful.key({alt}, "d", function()
bling.module.tabbed.pop()
end,
{description = "remove focused client from tabbing group",group = "tabs"}),
awful.key({modkey}, "Down", function()
awful.client.focus.bydirection("down")
bling.module.flash_focus.flashfocus(client.focus)
end,
{description = "focus down", group = "client"}),
awful.key({modkey}, "Up", function()
awful.client.focus.bydirection("up")
bling.module.flash_focus.flashfocus(client.focus)
end,
{description = "focus up", group = "client"}),
awful.key({modkey}, "Left", function()
awful.client.focus.bydirection("left")
bling.module.flash_focus.flashfocus(client.focus)
end,
{description = "focus left", group = "client"}),
awful.key({modkey}, "Right", function()
awful.client.focus.bydirection("right")
bling.module.flash_focus.flashfocus(client.focus)
end,
{description = "focus right", group = "client"}),
awful.key({modkey}, "j", function()
awful.client.focus.byidx(1)
end,
{description = "focus next by index", group = "client"}),
awful.key({modkey}, "k", function()
awful.client.focus.byidx(-1)
end,
{description = "focus previous by index", group = "client"}),
awful.key({modkey, "Shift"}, "j", function()
awful.client.swap.byidx(1)
end,
{description = "swap with next client by index", group = "client"}),
awful.key({modkey, "Shift"}, "k", function()
awful.client.swap.byidx(-1)
end,
{description = "swap with previous client by index", group = "client"}),
awful.key({modkey}, "u",
awful.client.urgent.jumpto,
{description = "jump to urgent client", group = "client"}),
awful.key({alt}, "Tab", function()
awesome.emit_signal("bling::window_switcher::turn_on")
end,
{description = "window switcher", group = "client"})
})
-- Hotkeys
--- Global key bindings
--- ~~~~~~~~~~~~~~~~~~~
awful.keyboard.append_global_keybindings({
-- Brightness Control
awful.key({}, "XF86MonBrightnessUp", function()
awful.spawn("brightnessctl set 5%+ -q")
end,
{description = "increase brightness", group = "hotkeys"}),
awful.key({}, "XF86MonBrightnessDown", function()
awful.spawn("brightnessctl set 5%- -q")
end,
{description = "decrease brightness", group = "hotkeys"}),
--- App
--- ~~~
-- Terminal
awful.key({ mod }, "Return", function()
awful.spawn(apps.default.terminal)
end, { description = "open terminal", group = "app" }),
-- Volume control
awful.key({}, "XF86AudioRaiseVolume", function()
helpers.volume_control(5)
end,
{description = "increase volume", group = "hotkeys"}),
awful.key({}, "XF86AudioLowerVolume", function()
helpers.volume_control(-5)
end,
{description = "decrease volume", group = "hotkeys"}),
awful.key({}, "XF86AudioMute", function()
helpers.volume_control(0)
end,
{description = "mute volume", group = "hotkeys"}),
--- App launcher
awful.key({ mod }, "d", function()
awful.spawn.with_shell(apps.default.app_launcher)
end, { description = "open app launcher", group = "app" }),
-- Music
awful.key({}, "XF86AudioPlay", function()
run_once("mpc toggle")
end,
{description = "toggle music", group = "hotkeys"}),
--- Code editor
awful.key({ mod, shift }, "e", function()
awful.spawn(apps.default.code_editor)
end, { description = "open code editor", group = "app" }),
awful.key({}, "XF86AudioPause", function()
run_once("mpc toggle")
end,
{description = "fuck you awesomeWM", group = "hotkeys"}),
--- File manager
awful.key({ mod, shift }, "f", function()
awful.spawn(apps.default.file_manager)
end, { description = "open file manager", group = "app" }),
awful.key({}, "XF86AudioPrev", function()
playerctl:previous()
end,
{description = "previous music", group = "hotkeys"}),
--- Web browser
awful.key({ mod }, "w", function()
awful.spawn(apps.default.web_browser)
end, { description = "open web browser", group = "app" }),
awful.key({}, "XF86AudioNext", function()
run_once("mpc next")
end,
{description = "next music", group = "hotkeys"}),
--- WM
--- ~~
--- Restart awesome
awful.key({ mod, ctrl }, "r", awesome.restart, { description = "reload awesome", group = "WM" }),
-- Screenshots
awful.key({}, "Print", function()
awful.spawn.with_shell("screensht full")
end,
{description = "take a full screenshot", group = "hotkeys"}),
--- Quit awesome
awful.key({ mod, ctrl }, "q", awesome.quit, { description = "quit awesome", group = "WM" }),
awful.key({alt, shift}, "s", function()
awful.spawn.with_shell("wackysnap area")
end,
{description = "take a area screenshot", group = "hotkeys"}),
--- Show help
awful.key({ mod }, "F1", hotkeys_popup.show_help, { description = "show Help", group = "WM" }),
-- Lockscreen
awful.key({modkey, ctrl}, "l", function()
lock_screen_show()
end,
{description = "lock screen", group = "hotkeys"}),
awful.key({ modkey, }, "z", function ()
awesome.emit_signal('module::quake_terminal:toggle')
end,
{description = "dropdown application", group = "hotkeys"})
--- Client
--- ~~~~~~
--- Focus client by direction
awful.key({ mod }, "k", function()
awful.client.focus.bydirection("up")
bling.module.flash_focus.flashfocus(client.focus)
end, { description = "focus up", group = "client" }),
awful.key({ mod }, "j", function()
awful.client.focus.bydirection("down")
bling.module.flash_focus.flashfocus(client.focus)
end, { description = "focus down", group = "client" }),
awful.key({ mod }, "h", function()
awful.client.focus.bydirection("left")
bling.module.flash_focus.flashfocus(client.focus)
end, { description = "focus left", group = "client" }),
awful.key({ mod }, "l", function()
awful.client.focus.bydirection("right")
bling.module.flash_focus.flashfocus(client.focus)
end, { description = "focus right", group = "client" }),
awful.key({ mod }, "Up", function()
awful.client.focus.bydirection("up")
bling.module.flash_focus.flashfocus(client.focus)
end, { description = "focus up", group = "client" }),
awful.key({ mod }, "Down", function()
awful.client.focus.bydirection("down")
bling.module.flash_focus.flashfocus(client.focus)
end, { description = "focus down", group = "client" }),
awful.key({ mod }, "Left", function()
awful.client.focus.bydirection("left")
bling.module.flash_focus.flashfocus(client.focus)
end, { description = "focus left", group = "client" }),
awful.key({ mod }, "Right", function()
awful.client.focus.bydirection("right")
bling.module.flash_focus.flashfocus(client.focus)
end, { description = "focus right", group = "client" }),
--- Resize focused client
awful.key({ mod, ctrl }, "k", function(c)
helpers.client.resize_client(client.focus, "up")
end, { description = "resize to the up", group = "client" }),
awful.key({ mod, ctrl }, "j", function(c)
helpers.client.resize_client(client.focus, "down")
end, { description = "resize to the down", group = "client" }),
awful.key({ mod, ctrl }, "h", function(c)
helpers.client.resize_client(client.focus, "left")
end, { description = "resize to the left", group = "client" }),
awful.key({ mod, ctrl }, "l", function(c)
helpers.client.resize_client(client.focus, "right")
end, { description = "resize to the right", group = "client" }),
awful.key({ mod, ctrl }, "Up", function(c)
helpers.client.resize_client(client.focus, "up")
end, { description = "resize to the up", group = "client" }),
awful.key({ mod, ctrl }, "Down", function(c)
helpers.client.resize_client(client.focus, "down")
end, { description = "resize to the down", group = "client" }),
awful.key({ mod, ctrl }, "Left", function(c)
helpers.client.resize_client(client.focus, "left")
end, { description = "resize to the left", group = "client" }),
awful.key({ mod, ctrl }, "Right", function(c)
helpers.client.resize_client(client.focus, "right")
end, { description = "resize to the right", group = "client" }),
--- Bling
--- ~~~~~
--- Add client to tabbed layout
awful.key({ alt }, "a", function()
bling.module.tabbed.pick_with_dmenu()
end, { description = "pick client to add to tab group", group = "tabs" }),
--- Remove client from tabbed layout
awful.key({ alt }, "d", function()
bling.module.tabbed.pop()
end, { description = "remove focused client from tabbing group", group = "tabs" }),
--- Cycle through client in tabbed layout
awful.key({ alt }, "s", function()
bling.module.tabbed.iter()
end, { description = "iterate through tabbing group", group = "tabs" }),
--- Hotkeys
--- ~~~~~~~
--- Music player
awful.key({ mod }, "grave", function()
awful.spawn.with_shell(apps.default.music_player)
end, { description = "open music client", group = "hotkeys" }),
--- Brightness Control
awful.key({}, "XF86MonBrightnessUp", function()
awful.spawn("brightnessctl set 5%+ -q")
end, { description = "increase brightness", group = "hotkeys" }),
awful.key({}, "XF86MonBrightnessDown", function()
awful.spawn("brightnessctl set 5%- -q")
end, { description = "decrease brightness", group = "hotkeys" }),
--- Volume control
awful.key({}, "XF86AudioRaiseVolume", function()
awful.spawn("pamixer -i 5")
end, { description = "increase volume", group = "hotkeys" }),
awful.key({}, "XF86AudioLowerVolume", function()
awful.spawn("pamixer -d 5")
end, { description = "decrease volume", group = "hotkeys" }),
awful.key({}, "XF86AudioMute", function()
awful.spawn("pamixer -t")
end, { description = "mute volume", group = "hotkeys" }),
--- Music
awful.key({}, "XF86AudioPlay", function()
playerctl_daemon:play_pause()
end, { description = "play pause music", group = "hotkeys" }),
awful.key({}, "XF86AudioPrev", function()
playerctl_daemon:previous()
end, { description = "previous music", group = "hotkeys" }),
awful.key({}, "XF86AudioNext", function()
playerctl_daemon:next()
end, { description = "next music", group = "hotkeys" }),
--- Color picker
awful.key({ mod, shift }, "x", function()
awful.spawn.easy_async_with_shell(apps.utils.color_picker, function() end)
end, { description = "open color picker", group = "hotkeys" }),
--- Screenshots
awful.key({}, "Print", function()
awful.spawn.easy_async_with_shell(apps.utils.full_screenshot, function() end)
end, { description = "take a full screenshot", group = "hotkeys" }),
awful.key({ alt }, "Print", function()
awful.spawn.easy_async_with_shell(apps.utils.area_screenshot, function() end)
end, { description = "take a area screenshot", group = "hotkeys" }),
--- Lockscreen
awful.key({ mod, alt }, "l", function()
lock_screen_show()
end, { description = "lock screen", group = "hotkeys" }),
--- Exit screen
awful.key({ mod }, "Escape", function()
awesome.emit_signal("module::exit_screen:show")
end, { description = "exit screen", group = "hotkeys" }),
})
-- Awesome stuff
awful.keyboard.append_global_keybindings({
awful.key({modkey}, "F1",
hotkeys_popup.show_help,
{description = "show help", group = "awesome"}),
awful.key({modkey, ctrl}, "r",
awesome.restart,
{description = "reload awesome", group = "awesome"}),
awful.key({modkey, ctrl}, "q",
awesome.quit,
{description = "quit awesome", group = "awesome"})
})
-- Layout Machi
awful.keyboard.append_global_keybindings({
awful.key({modkey}, ".", function()
machi.default_editor.start_interactive()
end,
{description = "edit the current layout if it is a machi layout", group = "layout"}),
awful.key({modkey}, "/", function()
machi.switcher.start(client.focus)
end,
{description = "switch between windows for a machi layout", group = "layout"})
})
awful.keyboard.append_global_keybindings({
-- Screen
awful.key({modkey, "Control"}, "j", function()
awful.screen.focus_relative(1)
end,
{description = "focus the next screen", group = "screen"}),
awful.key({modkey, "Control"}, "k", function()
awful.screen.focus_relative(-1)
end,
{description = "focus the previous screen", group = "screen"}),
-- Layout
awful.key({modkey}, "l", function()
awful.tag.incmwfact(0.05)
end,
{description = "increase master width factor", group = "layout"}),
awful.key({modkey}, "h", function()
awful.tag.incmwfact(-0.05)
end,
{description = "decrease master width factor", group = "layout"}),
awful.key({modkey, "Shift"}, "h", function()
awful.tag.incnmaster(1, nil, true)
end,
{description = "increase the number of master clients", group = "layout"}),
awful.key({modkey, "Shift"}, "l", function()
awful.tag.incnmaster(-1, nil, true)
end,
{description = "decrease the number of master clients", group = "layout"}),
awful.key({modkey, "Control"}, "h", function()
awful.tag.incncol(1, nil, true)
end,
{description = "increase the number of columns", group = "layout"}),
awful.key({modkey, "Control"}, "l", function()
awful.tag.incncol(-1, nil, true)
end,
{description = "decrease the number of columns", group = "layout"}),
awful.key({modkey}, "space", function()
awful.layout.inc(1)
end,
{description = "select next layout", group = "layout"}),
awful.key({modkey, "Shift"}, "space", function()
awful.layout.inc(-1)
end,
{description = "select previous layout", group = "layout"}),
-- Tag
awful.key({ modkey, alt}, "Left",
awful.tag.viewprev,
{description = "view previous", group = "tag"}),
awful.key({ modkey, alt}, "Right",
awful.tag.viewnext,
{description = "view next", group = "tag"}),
awful.key({ modkey}, "Escape",
awful.tag.history.restore,
{description = "go back", group = "tag"}),
-- Set Layout
awful.key({modkey, "Control"}, "w", function()
awful.layout.set(awful.layout.suit.max)
end,
{description = "set max layout", group = "tag"}),
awful.key({modkey}, "s", function()
awful.layout.set(awful.layout.suit.tile)
end,
{description = "set tile layout", group = "tag"}),
awful.key({modkey, shift}, "s", function()
awful.layout.set(awful.layout.suit.floating)
end,
{description = "set floating layout", group = "tag"}),
--Client
awful.key({modkey, "Control"}, "n", function()
local c = awful.client.restore()
-- Focus restored client
if c then
c:emit_signal("request::activate", "key.unminimize", {raise = true})
end
end,
{description = "restore minimized", group = "client"})
})
-- Client management keybinds
--- Client key bindings
--- ~~~~~~~~~~~~~~~~~~~
client.connect_signal("request::default_keybindings", function()
awful.keyboard.append_client_keybindings({
awful.key({modkey, "Shift"}, "f", function(c)
c.fullscreen = not c.fullscreen
c:raise()
end,
{description = "toggle fullscreen", group = "client"}),
awful.key({modkey}, "q", function(c)
c:kill()
end,
{description = "close", group = "client"}),
awful.key({modkey, "Control"}, "space",
awful.client.floating.toggle,
{description = "toggle floating", group = "client"}),
awful.key({modkey, "Control"}, "Return", function(c)
c:swap(awful.client.getmaster())
end,
{description = "move to master", group = "client"}),
awful.key({modkey}, "o", function(c)
c:move_to_screen() end,
{description = "move to screen", group = "client"}),
awful.key({modkey, shift}, "b", function(c)
c.floating = not c.floating
c.width = 400
c.height = 200
awful.placement.bottom_right(c)
c.sticky = not c.sticky
end,
{description = "toggle keep on top", group = "client"}),
awful.keyboard.append_client_keybindings({
-- Move or swap by direction
awful.key({ mod, shift }, "k", function(c)
helpers.client.move_client(c, "up")
end),
awful.key({ mod, shift }, "j", function(c)
helpers.client.move_client(c, "down")
end),
awful.key({ mod, shift }, "h", function(c)
helpers.client.move_client(c, "left")
end),
awful.key({ mod, shift }, "l", function(c)
helpers.client.move_client(c, "right")
end),
awful.key({modkey}, "n", function(c)
-- The client currently has the input focus, so it cannot be
-- minimized, since minimized clients can't have the focus.
c.minimized = true
end, {description = "minimize", group = "client"}),
awful.key({modkey}, "m", function(c)
c.maximized = not c.maximized
c:raise()
end, {description = "(un)maximize", group = "client"}),
awful.key({modkey, "Control"}, "m", function(c)
c.maximized_vertical = not c.maximized_vertical
c:raise()
end, {description = "(un)maximize vertically", group = "client"}),
awful.key({modkey, "Shift"}, "m", function(c)
c.maximized_horizontal = not c.maximized_horizontal
c:raise()
end, {description = "(un)maximize horizontally", group = "client"}),
awful.key({ mod, shift }, "Up", function(c)
helpers.client.move_client(c, "up")
end),
awful.key({ mod, shift }, "Down", function(c)
helpers.client.move_client(c, "down")
end),
awful.key({ mod, shift }, "Left", function(c)
helpers.client.move_client(c, "left")
end),
awful.key({ mod, shift }, "Right", function(c)
helpers.client.move_client(c, "right")
end),
-- On the fly padding change
awful.key({modkey, shift}, "=",
function() helpers.resize_padding(5) end,
{description = "add padding", group = "screen"}),
awful.key({modkey, shift}, "-",
function() helpers.resize_padding(-5) end,
{description = "subtract padding", group = "screen"}),
--- Relative move client
awful.key({ mod, shift, ctrl }, "j", function(c)
c:relative_move(0, dpi(20), 0, 0)
end),
-- On the fly useless gaps change
awful.key({modkey}, "=", function() helpers.resize_gaps(5) end,
{description = "add gaps", group = "screen"}),
awful.key({ mod, shift, ctrl }, "k", function(c)
c:relative_move(0, dpi(-20), 0, 0)
end),
awful.key({modkey}, "-", function() helpers.resize_gaps(-5) end,
{description = "subtract gaps", group = "screen"}),
-- Single tap: Center client
-- Double tap: Center client + Floating + Resize
awful.key({modkey}, "c", function(c)
awful.placement.centered(c, {
honor_workarea = true,
honor_padding = true
})
helpers.single_double_tap(nil, function()
helpers.float_and_resize(c, screen_width * 0.25,
screen_height * 0.28)
end)
end)
})
awful.key({ mod, shift, ctrl }, "h", function(c)
c:relative_move(dpi(-20), 0, 0, 0)
end),
awful.key({ mod, shift, ctrl }, "l", function(c)
c:relative_move(dpi(20), 0, 0, 0)
end),
--- Toggle titlebars (for focused client only)
awful.key({ mod }, "t", function(c)
decorations.cycle(c)
end, { description = "toggle titlebar", group = "client" }),
--- Toggle titlebars (for all visible clients in selected tag)
awful.key({ mod, shift }, "t", function(c)
local clients = awful.screen.focused().clients
for _, c in pairs(clients) do
decorations.cycle(c)
end
end, { description = "toggle titlebar", group = "client" }),
--- Toggle floating
awful.key({ mod, ctrl }, "space", awful.client.floating.toggle),
--- Toggle fullscreen
awful.key({ mod }, "f", function()
client.focus.fullscreen = not client.focus.fullscreen
client.focus:raise()
end),
--- Maximize windows
awful.key({ mod }, "m", function(c)
c.maximized = not c.maximized
end, { description = "toggle maximize", group = "client" }),
awful.key({ mod, ctrl }, "m", function(c)
c.maximized_vertical = not c.maximized_vertical
c:raise()
end, { description = "(un)maximize vertically", group = "client" }),
awful.key({ mod, shift }, "m", function(c)
c.maximized_horizontal = not c.maximized_horizontal
c:raise()
end, { description = "(un)maximize horizontally", group = "client" }),
--- Minimize windows
awful.key({ mod }, "n", function(c)
-- The client currently has the input focus, so it cannot be
-- minimized, since minimized clients can't have the focus.
c.minimized = true
end, { description = "minimize", group = "client" }),
--- Un-minimize windows
awful.key({ mod, ctrl }, "n", function()
local c = awful.client.restore()
-- Focus restored client
if c then
c:activate({ raise = true, context = "key.unminimize" })
end
end, { description = "restore minimized", group = "client" }),
--- Keep on top
awful.key({ mod }, "p", function(c)
c.ontop = not c.ontop
end),
--- Sticky
awful.key({ mod, shift }, "p", function(c)
c.sticky = not c.sticky
end),
--- Close window
awful.key({ mod }, "q", function()
client.focus:kill()
end),
--- Center window
awful.key({ mod }, "c", function()
awful.placement.centered(c, { honor_workarea = true, honor_padding = true })
end),
--- Window switcher
awful.key({ alt }, "Tab", function()
awesome.emit_signal("window_switcher::turn_on")
end),
})
end)
-- Num row keybinds
--- Layout
--- ~~~~~~
awful.keyboard.append_global_keybindings({
awful.key {
modifiers = {modkey},
keygroup = "numrow",
description = "only view tag",
group = "tag",
on_press = function(index)
local screen = awful.screen.focused()
local tag = screen.tags[index]
if tag then tag:view_only() end
end
}, awful.key {
modifiers = {modkey, "Control"},
keygroup = "numrow",
description = "toggle tag",
group = "tag",
on_press = function(index)
local screen = awful.screen.focused()
local tag = screen.tags[index]
if tag then awful.tag.viewtoggle(tag) end
end
}, awful.key {
modifiers = {modkey, "Shift"},
keygroup = "numrow",
description = "move focused client to tag",
group = "tag",
on_press = function(index)
if client.focus then
local tag = client.focus.screen.tags[index]
if tag then client.focus:move_to_tag(tag) end
end
end
}, awful.key {
modifiers = {modkey, "Control", "Shift"},
keygroup = "numrow",
description = "toggle focused client on tag",
group = "tag",
on_press = function(index)
if client.focus then
local tag = client.focus.screen.tags[index]
if tag then client.focus:toggle_tag(tag) end
end
end
}
--- Set tilling layout
awful.key({ mod }, "s", function()
awful.layout.set(awful.layout.suit.tile)
end, { description = "set tile layout", group = "layout" }),
--- Set floating layout
awful.key({ mod, shift }, "s", function()
awful.layout.set(awful.layout.suit.floating)
end, { description = "set floating layout", group = "layout" }),
--- Layout machi
awful.key({ mod }, ".", function()
machi.default_editor.start_interactive()
end, { description = "edit the current layout if it is a machi layout", group = "layout" }),
awful.key({ mod }, "/", function()
machi.switcher.start(client.focus)
end, { description = "switch between windows for a machi layout", group = "layout" }),
--- Number of columns
awful.key({ mod, alt }, "k", function()
awful.tag.incncol(1, nil, true)
end, { description = "increase the number of columns", group = "layout" }),
awful.key({ mod, alt }, "j", function()
awful.tag.incncol(-1, nil, true)
end, { description = "decrease the number of columns", group = "layout" }),
awful.key({ mod, alt }, "Up", function()
awful.tag.incncol(1, nil, true)
end, { description = "increase the number of columns", group = "layout" }),
awful.key({ mod, alt }, "Down", function()
awful.tag.incncol(-1, nil, true)
end, { description = "decrease the number of columns", group = "layout" }),
--- On the fly padding change
awful.key({ mod, shift }, "=", function()
helpers.client.resize_padding(5)
end, { description = "add padding", group = "layout" }),
awful.key({ mod, shift }, "-", function()
helpers.client.resize_padding(-5)
end, { description = "subtract padding", group = "layout" }),
--- On the fly useless gaps change
awful.key({ mod }, "=", function()
helpers.client.resize_gaps(5)
end, { description = "add gaps", group = "layout" }),
awful.key({ mod }, "-", function()
helpers.client.resize_gaps(-5)
end, { description = "subtract gaps", group = "layout" }),
})
-- Mouse bindings on desktop
------------------------------
--- Move through workspaces
--- ~~~~~~~~~~~~~~~~~~~~~~~
awful.keyboard.append_global_keybindings({
awful.key({ mod, alt }, "Left", awful.tag.viewprev, { description = "view previous", group = "tags" }),
awful.key({ mod, alt }, "Right", awful.tag.viewnext, { description = "view next", group = "tags" }),
awful.key({
modifiers = { mod },
keygroup = "numrow",
description = "only view tag",
group = "tags",
on_press = function(index)
local screen = awful.screen.focused()
local tag = screen.tags[index]
if tag then
tag:view_only()
end
end,
}),
awful.key({
modifiers = { mod, ctrl },
keygroup = "numrow",
description = "toggle tag",
group = "tags",
on_press = function(index)
local screen = awful.screen.focused()
local tag = screen.tags[index]
if tag then
awful.tag.viewtoggle(tag)
end
end,
}),
awful.key({
modifiers = { mod, shift },
keygroup = "numrow",
description = "move focused client to tag",
group = "tags",
on_press = function(index)
if client.focus then
local tag = client.focus.screen.tags[index]
if tag then
client.focus:move_to_tag(tag)
end
end
end,
}),
})
-- Screen
-----------
--awful.keyboard.append_global_keybindings({
-- No need for these (single screen setup)
--awful.key({ superkey, ctrlkey }, "j", function () awful.screen.focus_relative( 1) end,
--{description = "focus the next screen", group = "screen"}),
--awful.key({ superkey, ctrlkey }, "k", function () awful.screen.focus_relative(-1) end,
--{description = "focus the previous screen", group = "screen"}),
--})
--- Mouse bindings on desktop
--- ~~~~~~~~~~~~~~~~~~~~~~~~~
local main_menu = require("ui.main-menu")
awful.mouse.append_global_mousebindings({
--- Right click
awful.button({
modifiers = {},
button = 3,
on_press = function()
main_menu:toggle()
end,
}),
})
awful.mouse.append_global_mousebindings({
--- Left click
awful.button({}, 1, function()
naughty.destroy_all_notifications()
end),
-- Left click
awful.button({}, 1, function()
naughty.destroy_all_notifications()
if mymainmenu then
mymainmenu:hide()
end
end),
-- Middle click
awful.button({}, 2, function()
dashboard_toggle()
end),
-- Right click
awful.button({}, 3, function()
mymainmenu:toggle()
end),
-- Side key
awful.button({}, 4, awful.tag.viewprev),
awful.button({}, 5, awful.tag.viewnext)
--- Middle click
awful.button({}, 2, function()
awesome.emit_signal("central_panel::toggle", awful.screen.focused())
end),
})
-- Mouse buttons on the client
--------------------------------
--- Mouse buttons on the client
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~
client.connect_signal("request::default_mousebindings", function()
awful.mouse.append_client_mousebindings({
awful.button({}, 1, function(c)
c:activate{context = "mouse_click"}
end),
awful.button({modkey}, 1, function(c)
c:activate{context = "mouse_click", action = "mouse_move"}
end),
awful.button({modkey}, 3, function(c)
c:activate{context = "mouse_click", action = "mouse_resize"}
end)
})
awful.mouse.append_client_mousebindings({
awful.button({}, 1, function(c)
c:activate({ context = "mouse_click" })
end),
awful.button({ mod }, 1, function(c)
c:activate({ context = "mouse_click", action = "mouse_move" })
end),
awful.button({ mod }, 3, function(c)
c:activate({ context = "mouse_click", action = "mouse_resize" })
end),
})
end)

View File

@ -0,0 +1,33 @@
local awful = require("awful")
local beautiful = require("beautiful")
local bling = require("modules.bling")
local machi = require("modules.layout-machi")
beautiful.layout_machi = machi.get_icon()
--- Custom Layouts
local mstab = bling.layout.mstab
local centered = bling.layout.centered
local horizontal = bling.layout.horizontal
local equal = bling.layout.equalarea
local deck = bling.layout.deck
machi.editor.nested_layouts = {
["0"] = deck,
["1"] = awful.layout.suit.spiral,
["2"] = awful.layout.suit.fair,
["3"] = awful.layout.suit.fair.horizontal,
}
--- Set the layouts
tag.connect_signal("request::default_layouts", function()
awful.layout.append_default_layouts({
awful.layout.suit.tile,
awful.layout.suit.floating,
centered,
mstab,
horizontal,
machi.default_layout,
equal,
deck,
})
end)

View File

@ -1,96 +0,0 @@
-- Standard Awesome Library
local awful = require("awful")
local hotkeys_popup = require("awful.hotkeys_popup")
local beautiful = require("beautiful")
-- Helpers
local helpers = require("helpers")
-- Create a launcher widget and a main menu
awful.screen.connect_for_each_screen(function(s)
-- Submenu
awesomemenu = {
{
"Hotkeys",
function()
hotkeys_popup.show_help(nil, awful.screen.focused())
end,
},
{ "Manual", terminal .. " -e man awesome" },
{ "Edit Config", editor .. " " .. awesome.conffile },
{ "Restart", awesome.restart },
{
"Quit",
function()
awesome.quit()
end,
},
}
-- Powermenu
powermenu = {
{
"Power OFF",
function()
awful.spawn.with_shell("systemctl poweroff")
end,
},
{
"Reboot",
function()
awful.spawn.with_shell("systemctl reboot")
end,
},
{
"Suspend",
function()
lock_screen_show()
awful.spawn.with_shell("systemctl suspend")
end,
},
{
"Lock Screen",
function()
lock_screen_show()
end,
},
}
-- Mainmenu
mymainmenu = awful.menu({
items = {
{
"Terminal",
function()
awful.spawn.with_shell(terminal)
end,
},
{
"Code Editor",
function()
awful.spawn.with_shell(vscode)
end,
},
{
"File Manager",
function()
awful.spawn.with_shell(file_manager)
end,
},
{
"Web Browser",
function()
awful.spawn.with_shell(browser)
end,
},
{
"Music",
function()
awful.spawn.with_shell(music_client)
end,
},
{ "AwesomeWM", awesomemenu, beautiful.awesome_logo },
{ "Power Menu", powermenu },
},
})
end)

View File

@ -0,0 +1,135 @@
# ░█▀█░▀█▀░█▀▀░█▀█░█▄█░░░░█▀▀░█▀█░█▀█░█▀▀
# ░█▀▀░░█░░█░░░█░█░█░█░░░░█░░░█░█░█░█░█▀▀
# ░▀░░░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░░▀▀▀░▀▀▀░▀░▀░▀░░
#
# rxyhn X compositor configuration
# ░█▀▀░█▀█░█▀▄░█▀█░█▀▀░█▀▄░█▀▀
# ░█░░░█░█░█▀▄░█░█░█▀▀░█▀▄░▀▀█
# ░▀▀▀░▀▀▀░▀░▀░▀░▀░▀▀▀░▀░▀░▀▀▀
corner-radius = 12;
rounded-corners-exclude = [
# "class_g ?= 'rofi'",
"class_g ?= 'peek'",
"window_type *= 'dock'",
"window_type = 'menu'",
"window_type = 'tooltip'",
# "window_type = 'popup_menu'",
"window_type = 'dropdown_menu'"
];
# ░█▀▀░█░█░█▀█░█▀▄░█▀█░█░█
# ░▀▀█░█▀█░█▀█░█░█░█░█░█▄█
# ░▀▀▀░▀░▀░▀░▀░▀▀░░▀▀▀░▀░▀
shadow = true;
shadow-radius = 12;
shadow-opacity = 0.4;
shadow-offset-x = -12;
shadow-offset-y = -12;
shadow-color = "#000000";
xinerama-shadow-crop = true;
shadow-ignore-shaped = false;
shadow-exclude = [
"class_g = 'slop'",
"class_g ?= 'peek'",
"_NET_WM_WINDOW_TYPE@:a *= 'SPLASH'",
# "_NET_WM_WINDOW_TYPE@:a *= 'NOTIFICATION'",
# "window_type *= 'menu'",
"window_type = 'utility'",
"window_type = 'dropdown_menu'",
# "window_type = 'popup_menu'"
];
# ░█▀▀░█▀█░█▀▄░▀█▀░█▀█░█▀▀
# ░█▀▀░█▀█░█░█░░█░░█░█░█░█
# ░▀░░░▀░▀░▀▀░░▀▀▀░▀░▀░▀▀▀
fading = true;
fade-in-step = 0.03;
fade-out-step = 0.03;
fade-delta = 5;
no-fading-openclose = false
no-fading-destroyed-argb = true
fade-exclude = [
"class_g = 'slop'" # maim
]
# ░█▀█░█▀█░█▀█░█▀▀░▀█▀░▀█▀░█░█
# ░█░█░█▀▀░█▀█░█░░░░█░░░█░░░█░
# ░▀▀▀░▀░░░▀░▀░▀▀▀░▀▀▀░░▀░░░▀░
active-opacity = 1.0;
inactive-opacity = 1.0;
frame-opacity = 1.0;
inactive-dim = 0.0;
opacity-rule = [];
focus-exclude = [
#"class_g ?= 'rofi'"
#'_NET_WM_NAME@:s = "rofi"'
"class_g ?= 'slop'",
"name = 'rofi'",
"class_g ?= 'Steam'",
"_NET_WM_WINDOW_TYPE@:a *= 'MENU'",
"window_type *= 'menu'",
"window_type = 'utility'",
"window_type = 'dropdown_menu'",
"window_type = 'popup_menu'"
];
# ░█▀▄░█░░░█░█░█▀▄░█▀▄░▀█▀░█▀█░█▀▀
# ░█▀▄░█░░░█░█░█▀▄░█▀▄░░█░░█░█░█░█
# ░▀▀░░▀▀▀░▀▀▀░▀░▀░▀░▀░▀▀▀░▀░▀░▀▀▀
blur: {
method = "dual_kawase";
strength = 10;
background = false;
background-frame = true;
background-fixed = true;
}
blur-background-exclude = [
# Exclude everything except windows of type "splash"
# (Notice the inverse condition)
"window_type != 'splash'"
];
# ░█▀▀░█▀▀░█▀█░█▀▀░█▀▄░█▀█░█░░░░░█▀▀░█▀▀░▀█▀░▀█▀░▀█▀░█▀█░█▀▀░█▀▀
# ░█░█░█▀▀░█░█░█▀▀░█▀▄░█▀█░█░░░░░▀▀█░█▀▀░░█░░░█░░░█░░█░█░█░█░▀▀█
# ░▀▀▀░▀▀▀░▀░▀░▀▀▀░▀░▀░▀░▀░▀▀▀░░░▀▀▀░▀▀▀░░▀░░░▀░░▀▀▀░▀░▀░▀▀▀░▀▀▀
backend = "glx";
glx-no-stencil = false;
glx-copy-from-front = false;
use-damage = true;
vsync = true;
detect-rounded-corners = true;
detect-client-leader = true;
detect-transient = true;
unredir-if-possible = true;
wintypes:
{
tooltip = { fade = true; full-shadow = true; focus = true; blur-background = false;};
menu = { full-shadow = true;};
popup_menu = { full-shadow = true;};
utility = {full-shadow = true;};
toolbar = {full-shadow = true;};
normal = {full-shadow = true;};
notification = {full-shadow = true;};
dialog = {full-shadow = true};
dock = {full-shadow = true;};
dropdown_menu = { full-shadow = true;};
};

View File

@ -0,0 +1,142 @@
configuration {
font: "Roboto Medium 10";
show-icons: true;
icon-theme: "WhiteSur-dark";
display-drun: "";
drun-display-format: "{icon} {name}";
disable-history: false;
sidebar-mode: false;
click-to-exit: true;
location: 6;
}
* {
BG: #06111599;
BGA: #061115;
FG: #D9D7D6ff;
UGT: #DF5B61ff;
}
window {
transparency: "real";
background-color: @BG;
text-color: @FG;
border-radius: 8px;
width: 700px;
x-offset: 0;
y-offset: -45px;
}
prompt {
enabled: true;
background-color: @BG;
text-color: @FG;
}
textbox-prompt-colon {
expand: false;
str: "";
background-color: @BGA;
text-color: @FG;
padding: 10px 0px 0px 12px;
font: "Material Icons Round 16";
}
entry {
background-color: @BGA;
text-color: @FG;
placeholder-color: @FG;
expand: true;
horizontal-align: 0;
placeholder: "Type here to search";
font: "Roboto Bold 12";
blink: true;
padding: 10px;
}
inputbar {
children: [ textbox-prompt-colon, entry ];
spacing: 0px;
background-color: @BGA;
text-color: @FG;
expand: false;
border-radius: 8px;
position: center;
}
case-indicator {
background-color: @BG;
text-color: @FG;
spacing: 0;
}
listview {
background-color: #00000000;
columns: 5;
lines: 5;
spacing: 4px;
cycle: false;
dynamic: true;
layout: vertical;
}
mainbox {
background-color: @BG;
children: [ inputbar, listview ];
spacing: 40px;
padding: 30px;
}
element {
background-color: #00000000;
text-color: @FG;
orientation: vertical;
border-radius: 8px;
padding: 20px;
}
element-icon {
background-color: inherit;
text-color: inherit;
horizontal-align: 0.5;
vertical-align: 0.5;
size: 48px;
}
element-text {
background-color: inherit;
text-color: inherit;
expand: true;
horizontal-align: 0.5;
vertical-align: 0.5;
}
element normal.urgent,
element alternate.urgent {
background-color: @UGT;
text-color: @FG;
border-radius: 8px;
}
element normal.active,
element alternate.active {
background-color: @BGA;
text-color: @FG;
}
element selected {
background-color: @BGA;
text-color: @FG;
border-radius: 8px;
}
element selected.urgent {
background-color: @UGT;
text-color: @FG;
}
element selected.active {
background-color: @BGA;
color: @FG;
}

93
config/awesome/configuration/ruled.lua Normal file → Executable file
View File

@ -1,39 +1,32 @@
-- Standard awesome library
local gears = require("gears")
local awful = require("awful")
-- Theme handling library
local beautiful = require("beautiful")
-- Notification handling library
local naughty = require("naughty")
-- Ruled
local ruled = require("ruled")
-- Helpers
local helpers = require("helpers")
-- Get screen geometry
--- Get screen geometry
local screen_width = awful.screen.focused().geometry.width
local screen_height = awful.screen.focused().geometry.height
ruled.client.connect_signal("request::rules", function()
-- Global
--- Global
ruled.client.append_rule({
id = "global",
rule = {},
properties = {
focus = awful.client.focus.filter,
raise = true,
size_hints_honor = false,
screen = awful.screen.preferred,
honor_workarea = true,
honor_padding = true,
-- screen = awful.screen.preferred,
screen = awful.screen.focused,
focus = awful.client.focus.filter,
titlebars_enabled = beautiful.titlebar_enabled,
placement = awful.placement.no_overlap + awful.placement.no_offscreen,
},
})
-- Tasklist order
--- Tasklist order
ruled.client.append_rule({
id = "tasklist_order",
rule = {},
@ -41,20 +34,14 @@ ruled.client.connect_signal("request::rules", function()
callback = awful.client.setslave,
})
-- Titlebar rules
--- Titlebar rules
ruled.client.append_rule({
id = "titlebars",
rule_any = {
class = {
"discord",
"Spotify",
"Org.gnome.Nautilus",
},
type = {
"splash",
},
name = {
"^discord.com is sharing your screen.$", -- Discord (running in browser) screen sharing popup
"Peek",
},
},
properties = {
@ -62,12 +49,12 @@ ruled.client.connect_signal("request::rules", function()
},
})
-- Float
--- Float
ruled.client.append_rule({
id = "floating",
rule_any = {
instance = {
"Devtools", -- Firefox devtools
"Devtools", --- Firefox devtools
},
class = {
"Lxappearance",
@ -86,10 +73,10 @@ ruled.client.connect_signal("request::rules", function()
"dialog",
},
},
properties = { floating = true, placement = helpers.centered_client_placement },
properties = { floating = true, placement = helpers.client.centered_client_placement },
})
-- Centered
--- Centered
ruled.client.append_rule({
id = "centered",
rule_any = {
@ -97,19 +84,35 @@ ruled.client.connect_signal("request::rules", function()
"dialog",
},
class = {
-- "discord",
--- "discord",
},
role = {
"GtkFileChooserDialog",
"conversation",
},
},
properties = { placement = helpers.centered_client_placement },
properties = { placement = helpers.client.centered_client_placement },
})
-- Music clients (usually a terminal running ncmpcpp)
--- Music clients (usually a terminal running ncmpcpp)
ruled.client.append_rule({
rule_any = {
class = {
"music",
},
instance = {
"music",
},
},
properties = {
floating = true,
width = screen_width * 0.40,
height = screen_height * 0.42,
placement = helpers.client.centered_client_placement,
},
})
-- Image viewers
--- Image viewers
ruled.client.append_rule({
rule_any = {
class = {
@ -126,32 +129,4 @@ ruled.client.connect_signal("request::rules", function()
awful.placement.centered(c, { honor_padding = true, honor_workarea = true })
end,
})
-- Mpv
ruled.client.append_rule({
rule = { class = "mpv" },
properties = {},
callback = function(c)
-- make it floating, ontop and move it out of the way if the current tag is maximized
if awful.layout.get(awful.screen.focused()) == awful.layout.suit.floating then
c.floating = true
c.ontop = true
c.width = screen_width * 0.30
c.height = screen_height * 0.35
awful.placement.bottom_right(c, {
honor_padding = true,
honor_workarea = true,
margins = { bottom = beautiful.useless_gap * 2, right = beautiful.useless_gap * 2 },
})
awful.titlebar.hide(c, beautiful.titlebar_pos)
end
-- restore `ontop` after fullscreen is disabled
c:connect_signal("property::fullscreen", function()
if not c.fullscreen then
c.ontop = true
end
end)
end,
})
end)

View File

@ -0,0 +1,9 @@
local awful = require("awful")
--- Tags
--- ~~~~
screen.connect_signal("request::desktop_decoration", function(s)
--- Each screen has its own tag table.
awful.tag({ "1", "2", "3", "4", "5", "6" }, s, awful.layout.layouts[1])
end)

View File

@ -1,678 +0,0 @@
-- helpers.lua
-- Functions that you use more than once and in different files would
-- be nice to define here.
local awful = require("awful")
local gears = require("gears")
local beautiful = require("beautiful")
local xresources = require("beautiful.xresources")
local dpi = xresources.apply_dpi
local wibox = require("wibox")
local naughty = require("naughty")
local helpers = {}
function helpers.contains(_table, _c)
for _, c in ipairs(_table) do
if _c == c then
return true
end
end
return false
end
function helpers.find(rule)
local function matcher(c)
return awful.rules.match(c, rule)
end
local clients = client.get()
local findex = gears.table.hasitem(clients, client.focus) or 1
local start = gears.math.cycle(#clients, findex + 1)
local matches = {}
for c in awful.client.iterate(matcher, start) do
matches[#matches + 1] = c
end
return matches
end
-- Adds a maximized mask to a screen
function helpers.screen_mask(s, bg)
local mask = wibox({
visible = false,
ontop = true,
type = "splash",
screen = s,
})
awful.placement.maximize(mask)
mask.bg = bg
return mask
end
function helpers.custom_shape(cr, width, height)
cr:move_to(0, height / 25)
cr:line_to(height / 25, 0)
cr:line_to(width, 0)
cr:line_to(width, height - height / 25)
cr:line_to(width - height / 25, height)
cr:line_to(0, height)
cr:close_path()
end
-- Resize gaps on the fly
helpers.resize_gaps = function(amt)
local t = awful.screen.focused().selected_tag
t.gap = t.gap + tonumber(amt)
awful.layout.arrange(awful.screen.focused())
end
-- Resize padding on the fly
helpers.resize_padding = function(amt)
local s = awful.screen.focused()
local l = s.padding.left
local r = s.padding.right
local t = s.padding.top
local b = s.padding.bottom
s.padding = {
left = l + amt,
right = r + amt,
top = t + amt,
bottom = b + amt,
}
awful.layout.arrange(awful.screen.focused())
end
-- Create rounded rectangle shape (in one line)
helpers.rrect = function(radius)
return function(cr, width, height)
gears.shape.rounded_rect(cr, width, height, radius)
end
end
helpers.squircle = function(rate, delta)
return function(cr, width, height)
gears.shape.squircle(cr, width, height, rate, delta)
end
end
helpers.psquircle = function(rate, delta, tl, tr, br, bl)
return function(cr, width, height)
gears.shape.partial_squircle(cr, width, height, tl, tr, br, bl, rate, delta)
end
end
-- Create pi
helpers.pie = function(width, height, start_angle, end_angle, radius)
return function(cr)
gears.shape.pie(cr, width, height, start_angle, end_angle, radius)
end
end
-- Create parallelogram
helpers.prgram = function(height, base)
return function(cr, width)
gears.shape.parallelogram(cr, width, height, base)
end
end
-- Create partially rounded rect
helpers.prrect = function(radius, tl, tr, br, bl)
return function(cr, width, height)
gears.shape.partially_rounded_rect(cr, width, height, tl, tr, br, bl, radius)
end
end
-- Create rounded bar
helpers.rbar = function(width, height)
return function(cr)
gears.shape.rounded_bar(cr, width, height)
end
end
-- Markup helper
function helpers.colorize_text(txt, fg)
return "<span foreground='" .. fg .. "'>" .. txt .. "</span>"
end
function helpers.client_menu_toggle()
local instance = nil
return function()
if instance and instance.wibox.visible then
instance:hide()
instance = nil
else
instance = awful.menu.clients({ theme = { width = dpi(250) } })
end
end
end
-- Escapes a string so that it can be displayed inside pango markup
-- tags. Modified from:
-- https://github.com/kernelsauce/turbo/blob/master/turbo/escape.lua
function helpers.pango_escape(s)
return (string.gsub(s, "[&<>]", { ["&"] = "&amp;", ["<"] = "&lt;", [">"] = "&gt;" }))
end
function helpers.vertical_pad(height)
return wibox.widget({
forced_height = height,
layout = wibox.layout.fixed.vertical,
})
end
function helpers.horizontal_pad(width)
return wibox.widget({
forced_width = width,
layout = wibox.layout.fixed.horizontal,
})
end
-- Maximizes client and also respects gaps
function helpers.maximize(c)
c.maximized = not c.maximized
if c.maximized then
awful.placement.maximize(c, {
honor_padding = true,
honor_workarea = true,
margins = beautiful.useless_gap * 2,
})
end
c:raise()
end
function helpers.move_to_edge(c, direction)
-- local workarea = awful.screen.focused().workarea
-- local client_geometry = c:geometry()
if direction == "up" then
local old_x = c:geometry().x
awful.placement.top(c, {
honor_padding = true,
honor_workarea = true,
honor_padding = true,
})
c.x = old_x
-- c:geometry({ nil, y = workarea.y + beautiful.screen_margin * 2, nil, nil })
elseif direction == "down" then
local old_x = c:geometry().x
awful.placement.bottom(c, {
honor_padding = true,
honor_workarea = true,
honor_padding = true,
})
c.x = old_x
-- c:geometry({ nil, y = workarea.height + workarea.y - client_geometry.height - beautiful.screen_margin * 2 - beautiful.border_width * 2, nil, nil })
elseif direction == "left" then
local old_y = c:geometry().y
awful.placement.left(c, {
honor_padding = true,
honor_workarea = true,
honor_padding = true,
})
c.y = old_y
-- c:geometry({ x = workarea.x + beautiful.screen_margin * 2, nil, nil, nil })
elseif direction == "right" then
local old_y = c:geometry().y
awful.placement.right(c, {
honor_padding = true,
honor_workarea = true,
honor_padding = true,
})
c.y = old_y
-- c:geometry({ x = workarea.width + workarea.x - client_geometry.width - beautiful.screen_margin * 2 - beautiful.border_width * 2, nil, nil, nil })
end
end
local double_tap_timer = nil
function helpers.single_double_tap(single_tap_function, double_tap_function)
if double_tap_timer then
double_tap_timer:stop()
double_tap_timer = nil
double_tap_function()
-- naughty.notify({text = "We got a double tap"})
return
end
double_tap_timer = gears.timer.start_new(0.20, function()
double_tap_timer = nil
-- naughty.notify({text = "We got a single tap"})
if single_tap_function then
single_tap_function()
end
return false
end)
end
-- Used as a custom command in rofi to move a window into the current tag
-- instead of following it.
-- Rofi has access to the X window id of the client.
function helpers.rofi_move_client_here(window)
local win = function(c)
return awful.rules.match(c, { window = window })
end
for c in awful.client.iterate(win) do
c.minimized = false
c:move_to_tag(mouse.screen.selected_tag)
client.focus = c
c:raise()
end
end
-- Add a hover cursor to a widget by changing the cursor on
-- mouse::enter and mouse::leave
-- You can find the names of the available cursors by opening any
-- cursor theme and looking in the "cursors folder"
-- For example: "hand1" is the cursor that appears when hovering over
-- links
function helpers.add_hover_cursor(w, hover_cursor)
local original_cursor = "left_ptr"
w:connect_signal("mouse::enter", function()
local w = _G.mouse.current_wibox
if w then
w.cursor = hover_cursor
end
end)
w:connect_signal("mouse::leave", function()
local w = _G.mouse.current_wibox
if w then
w.cursor = original_cursor
end
end)
end
-- Tag back and forth:
-- If you try to focus the tag you are already at, go back to the previous tag.
-- Useful for quick switching after for example checking an incoming chat
-- message at tag 2 and coming back to your work at tag 1 with the same
-- keypress.
-- Also focuses urgent clients if they exist in the tag. This fixes the issue
-- (visual mismatch) where after switching to a tag which includes an urgent
-- client, the urgent client is unfocused but still covers all other windows
-- (even the currently focused window).
function helpers.tag_back_and_forth(tag_index)
local s = mouse.screen
local tag = s.tags[tag_index]
if tag then
if tag == s.selected_tag then
awful.tag.history.restore()
else
tag:view_only()
end
local urgent_clients = function(c)
return awful.rules.match(c, { urgent = true, first_tag = tag })
end
for c in awful.client.iterate(urgent_clients) do
client.focus = c
c:raise()
end
end
end
-- Resize DWIM (Do What I Mean)
-- Resize client or factor
-- Constants --
local floating_resize_amount = dpi(20)
local tiling_resize_factor = 0.05
---------------
function helpers.resize_dwim(c, direction)
if awful.layout.get(mouse.screen) == awful.layout.suit.floating or (c and c.floating) then
if direction == "up" then
c:relative_move(0, 0, 0, -floating_resize_amount)
elseif direction == "down" then
c:relative_move(0, 0, 0, floating_resize_amount)
elseif direction == "left" then
c:relative_move(0, 0, -floating_resize_amount, 0)
elseif direction == "right" then
c:relative_move(0, 0, floating_resize_amount, 0)
end
else
if direction == "up" then
awful.client.incwfact(-tiling_resize_factor)
elseif direction == "down" then
awful.client.incwfact(tiling_resize_factor)
elseif direction == "left" then
awful.tag.incmwfact(-tiling_resize_factor)
elseif direction == "right" then
awful.tag.incmwfact(tiling_resize_factor)
end
end
end
-- Move client to screen edge, respecting the screen workarea
function helpers.move_to_edge(c, direction)
local workarea = awful.screen.focused().workarea
if direction == "up" then
c:geometry({ nil, y = workarea.y + beautiful.useless_gap * 2, nil, nil })
elseif direction == "down" then
c:geometry({
nil,
y = workarea.height
+ workarea.y
- c:geometry().height
- beautiful.useless_gap * 2
- beautiful.border_width * 2,
nil,
nil,
})
elseif direction == "left" then
c:geometry({ x = workarea.x + beautiful.useless_gap * 2, nil, nil, nil })
elseif direction == "right" then
c:geometry({
x = workarea.width
+ workarea.x
- c:geometry().width
- beautiful.useless_gap * 2
- beautiful.border_width * 2,
nil,
nil,
nil,
})
end
end
-- Move client DWIM (Do What I Mean)
-- Move to edge if the client / layout is floating
-- Swap by index if maximized
-- Else swap client by direction
function helpers.move_client_dwim(c, direction)
if c.floating or (awful.layout.get(mouse.screen) == awful.layout.suit.floating) then
helpers.move_to_edge(c, direction)
elseif awful.layout.get(mouse.screen) == awful.layout.suit.max then
if direction == "up" or direction == "left" then
awful.client.swap.byidx(-1, c)
elseif direction == "down" or direction == "right" then
awful.client.swap.byidx(1, c)
end
else
awful.client.swap.bydirection(direction, c, nil)
end
end
-- Make client floating and snap to the desired edge
function helpers.float_and_edge_snap(c, direction)
-- if not c.floating then
-- c.floating = true
-- end
naughty.notify({ text = "double tap" })
c.floating = true
local workarea = awful.screen.focused().workarea
if direction == "up" then
local axis = "horizontally"
local f = awful.placement.scale + awful.placement.top + (axis and awful.placement["maximize_" .. axis] or nil)
local geo = f(client.focus, {
honor_padding = true,
honor_workarea = true,
to_percent = 0.5,
})
elseif direction == "down" then
local axis = "horizontally"
local f = awful.placement.scale
+ awful.placement.bottom
+ (axis and awful.placement["maximize_" .. axis] or nil)
local geo = f(client.focus, {
honor_padding = true,
honor_workarea = true,
to_percent = 0.5,
})
elseif direction == "left" then
local axis = "vertically"
local f = awful.placement.scale + awful.placement.left + (axis and awful.placement["maximize_" .. axis] or nil)
local geo = f(client.focus, {
honor_padding = true,
honor_workarea = true,
to_percent = 0.5,
})
elseif direction == "right" then
local axis = "vertically"
local f = awful.placement.scale + awful.placement.right + (axis and awful.placement["maximize_" .. axis] or nil)
local geo = f(client.focus, {
honor_padding = true,
honor_workarea = true,
to_percent = 0.5,
})
end
end
-- Rounds a number to any number of decimals
function helpers.round(number, decimals)
local power = 10 ^ decimals
return math.floor(number * power) / power
end
function helpers.fake_escape()
root.fake_input("key_press", "Escape")
root.fake_input("key_release", "Escape")
end
function helpers.pad(size)
local str = ""
for i = 1, size do
str = str .. " "
end
local pad = wibox.widget.textbox(str)
return pad
end
function helpers.float_and_resize(c, width, height)
c.width = width
c.height = height
awful.placement.centered(c, { honor_workarea = true, honor_padding = true })
awful.client.property.set(c, "floating_geometry", c:geometry())
c.floating = true
c:raise()
end
function helpers.centered_client_placement(c)
return gears.timer.delayed_call(function()
awful.placement.centered(c, { honor_padding = true, honor_workarea = true })
end)
end
-- Useful for periodically checking the output of a command that
-- requires internet access.
-- Ensures that `command` will be run EXACTLY once during the desired
-- `interval`, even if awesome restarts multiple times during this time.
-- Saves output in `output_file` and checks its last modification
-- time to determine whether to run the command again or not.
-- Passes the output of `command` to `callback` function.
function helpers.remote_watch(command, interval, output_file, callback)
local run_the_thing = function()
-- Pass output to callback AND write it to file
awful.spawn.easy_async_with_shell(command .. " | tee " .. output_file, function(out)
callback(out)
end)
end
local timer
timer = gears.timer({
timeout = interval,
call_now = true,
autostart = true,
single_shot = false,
callback = function()
awful.spawn.easy_async_with_shell(
"date -r " .. output_file .. " +%s",
function(last_update, _, __, exitcode)
-- Probably the file does not exist yet (first time
-- running after reboot)
if exitcode == 1 then
run_the_thing()
return
end
local diff = os.time() - tonumber(last_update)
if diff >= interval then
run_the_thing()
else
-- Pass the date saved in the file since it is fresh enough
awful.spawn.easy_async_with_shell("cat " .. output_file, function(out)
callback(out)
end)
-- Schedule an update for when the remaining time to complete the interval passes
timer:stop()
gears.timer.start_new(interval - diff, function()
run_the_thing()
timer:again()
end)
end
end
)
end,
})
end
-- Volume Control
function helpers.volume_control(step)
local cmd
if step == 0 then
cmd = "pactl set-sink-mute @DEFAULT_SINK@ toggle"
else
sign = step > 0 and "+" or ""
cmd = "pactl set-sink-mute @DEFAULT_SINK@ 0 && pactl set-sink-volume @DEFAULT_SINK@ "
.. sign
.. tostring(step)
.. "%"
end
awful.spawn.with_shell(cmd)
end
function helpers.music_control(state)
local cmd
if state == "toggle" then
cmd = "playerctl -p spotify,mpd play-pause"
elseif state == "prev" then
cmd = "playerctl -p spotify,mpd previous"
elseif state == "next" then
cmd = "playerctl -p spotify,mpd next"
end
awful.spawn.with_shell(cmd)
end
function helpers.send_key(c, key)
awful.spawn.with_shell("xdotool key --window " .. tostring(c.window) .. " " .. key)
end
function helpers.send_key_sequence(c, seq)
awful.spawn.with_shell("xdotool type --delay 5 --window " .. tostring(c.window) .. " " .. seq)
end
local prompt_font = beautiful.prompt_font
function helpers.prompt(action, textbox, prompt, callback)
if action == "run" then
awful.prompt.run({
prompt = prompt,
textbox = textbox,
font = prompt_font,
done_callback = callback,
exe_callback = awful.spawn,
completion_callback = awful.completion.shell,
history_path = awful.util.get_cache_dir() .. "/history",
})
elseif action == "web_search" then
awful.prompt.run({
prompt = prompt,
textbox = textbox,
font = prompt_font,
history_path = awful.util.get_cache_dir() .. "/history_web",
done_callback = callback,
exe_callback = function(input)
if not input or #input == 0 then
return
end
awful.spawn.with_shell("noglob " .. web_search_cmd .. "'" .. input .. "'")
naughty.notify({
title = "Searching the web for",
text = input,
urgency = "low",
})
end,
})
end
end
-- Given a `match` condition, returns an array with clients that match it, or
-- just the first found client if `first_only` is true
function helpers.find_clients(match, first_only)
local matcher = function(c)
return awful.rules.match(c, match)
end
if first_only then
for c in awful.client.iterate(matcher) do
return c
end
else
local clients = {}
for c in awful.client.iterate(matcher) do
table.insert(clients, c)
end
return clients
end
return nil
end
-- Given a `match` condition, calls the specified function `f_do` on all the
-- clients that match it
function helpers.find_clients_and_do(match, f_do)
local matcher = function(c)
return awful.rules.match(c, match)
end
for c in awful.client.iterate(matcher) do
f_do(c)
end
end
function helpers.run_or_raise(match, move, spawn_cmd, spawn_args)
local matcher = function(c)
return awful.rules.match(c, match)
end
-- Find and raise
local found = false
for c in awful.client.iterate(matcher) do
found = true
c.minimized = false
if move then
c:move_to_tag(mouse.screen.selected_tag)
client.focus = c
else
c:jump_to()
end
break
end
-- Spawn if not found
if not found then
awful.spawn(spawn_cmd, spawn_args)
end
end
-- Run raise or minimize a client (scratchpad style)
-- Depends on helpers.run_or_raise
-- If it not running, spawn it
-- If it is running, focus it
-- If it is focused, minimize it
function helpers.scratchpad(match, spawn_cmd, spawn_args)
local cf = client.focus
if cf and awful.rules.match(cf, match) then
cf.minimized = true
else
helpers.run_or_raise(match, true, spawn_cmd, spawn_args)
end
end
return helpers

116
config/awesome/helpers/client.lua Executable file
View File

@ -0,0 +1,116 @@
local awful = require("awful")
local gears = require("gears")
local beautiful = require("beautiful")
local xresources = require("beautiful.xresources")
local dpi = xresources.apply_dpi
local capi = { client = client, mouse = mouse }
local _client = {}
-- Resize client or factor
local floating_resize_amount = dpi(20)
local tiling_resize_factor = 0.05
function _client.resize_client(c, direction)
if c and c.floating or awful.layout.get(capi.mouse.screen) == awful.layout.suit.floating then
if direction == "up" then
c:relative_move(0, 0, 0, -floating_resize_amount)
elseif direction == "down" then
c:relative_move(0, 0, 0, floating_resize_amount)
elseif direction == "left" then
c:relative_move(0, 0, -floating_resize_amount, 0)
elseif direction == "right" then
c:relative_move(0, 0, floating_resize_amount, 0)
end
elseif awful.layout.get(capi.mouse.screen) ~= awful.layout.suit.floating then
if direction == "up" then
awful.client.incwfact(-tiling_resize_factor)
elseif direction == "down" then
awful.client.incwfact(tiling_resize_factor)
elseif direction == "left" then
awful.tag.incmwfact(-tiling_resize_factor)
elseif direction == "right" then
awful.tag.incmwfact(tiling_resize_factor)
end
end
end
-- Move client to screen edge, respecting the screen workarea
function _client.move_to_edge(c, direction)
local workarea = awful.screen.focused().workarea
if direction == "up" then
c:geometry({ nil, y = workarea.y + beautiful.useless_gap * 2, nil, nil })
elseif direction == "down" then
c:geometry({
nil,
y = workarea.height
+ workarea.y
- c:geometry().height
- beautiful.useless_gap * 2
- beautiful.border_width * 2,
nil,
nil,
})
elseif direction == "left" then
c:geometry({ x = workarea.x + beautiful.useless_gap * 2, nil, nil, nil })
elseif direction == "right" then
c:geometry({
x = workarea.width
+ workarea.x
- c:geometry().width
- beautiful.useless_gap * 2
- beautiful.border_width * 2,
nil,
nil,
nil,
})
end
end
-- Move client DWIM (Do What I Mean)
-- Move to edge if the client / layout is floating
-- Swap by index if maximized
-- Else swap client by direction
function _client.move_client(c, direction)
if c.floating or (awful.layout.get(capi.mouse.screen) == awful.layout.suit.floating) then
_client.move_to_edge(c, direction)
elseif awful.layout.get(capi.mouse.screen) == awful.layout.suit.max then
if direction == "up" or direction == "left" then
awful.client.swap.byidx(-1, c)
elseif direction == "down" or direction == "right" then
awful.client.swap.byidx(1, c)
end
else
awful.client.swap.bydirection(direction, c, nil)
end
end
function _client.centered_client_placement(c)
return gears.timer.delayed_call(function()
awful.placement.centered(c, { honor_padding = true, honor_workarea = true })
end)
end
-- Resize gaps on the fly
_client.resize_gaps = function(amt)
local t = awful.screen.focused().selected_tag
t.gap = t.gap + tonumber(amt)
awful.layout.arrange(awful.screen.focused())
end
-- Resize padding on the fly
_client.resize_padding = function(amt)
local s = awful.screen.focused()
local l = s.padding.left
local r = s.padding.right
local t = s.padding.top
local b = s.padding.bottom
s.padding = {
left = l + amt,
right = r + amt,
top = t + amt,
bottom = b + amt,
}
awful.layout.arrange(awful.screen.focused())
end
return _client

207
config/awesome/helpers/color.lua Executable file
View File

@ -0,0 +1,207 @@
local color_libary = require("modules.color")
local tonumber = tonumber
local string = string
local math = math
local type = type
local floor = math.floor
local max = math.max
local min = math.min
local pow = math.pow
local random = math.random
local abs = math.abs
local format = string.format
local _color = {}
local function round(x, p)
local power = 10 ^ (p or 0)
return (x * power + 0.5 - (x * power + 0.5) % 1) / power
end
-- Returns a value that is clipped to interval edges if it falls outside the interval
local function clip(num, min_num, max_num)
return max(min(num, max_num), min_num)
end
-- Converts the given hex color to rgba
function _color.hex_to_rgb(color)
color = color:gsub("#", "")
return {
r = tonumber("0x" .. color:sub(1, 2)),
g = tonumber("0x" .. color:sub(3, 4)),
b = tonumber("0x" .. color:sub(5, 6)),
a = #color == 8 and tonumber("0x" .. color:sub(7, 8)) or 255,
}
end
-- Converts the given rgba color to hex
function _color.rgb_to_hex(color)
local r = clip(color.r or color[1], 0, 255)
local g = clip(color.g or color[2], 0, 255)
local b = clip(color.b or color[3], 0, 255)
local a = clip(color.a or color[4] or 255, 0, 255)
return "#" .. format("%02x%02x%02x%02x", floor(r), floor(g), floor(b), floor(a))
end
-- Converts the given hex color to hsv
function _color.hex_to_hsv(color)
local color = _color.hex_to_rgb(color)
local C_max = max(color.r, color.g, color.b)
local C_min = min(color.r, color.g, color.b)
local delta = C_max - C_min
local H, S, V
if delta == 0 then
H = 0
elseif C_max == color.r then
H = 60 * (((color.g - color.b) / delta) % 6)
elseif C_max == color.g then
H = 60 * (((color.b - color.r) / delta) + 2)
elseif C_max == color.b then
H = 60 * (((color.r - color.g) / delta) + 4)
end
if C_max == 0 then
S = 0
else
S = delta / C_max
end
V = C_max
return { h = H, s = S * 100, v = V * 100 }
end
--- Try to guess if a color is dark or light.
function _color.is_dark(color)
color = color_libary.color({ hex = color })
return color.l <= 0.4
end
--- Check if a color is opaque.
function _color.is_opaque(color)
color = color_libary.color({ hex = color })
return color.a == 0
end
-- Calculates the relative luminance of the given color
function _color.relative_luminance(color)
local function from_sRGB(u)
return u <= 0.0031308 and 25 * u / 323 or pow(((200 * u + 11) / 211), 12 / 5)
end
color = color_libary.color({ hex = color })
return 0.2126 * from_sRGB(color.r) + 0.7152 * from_sRGB(color.g) + 0.0722 * from_sRGB(color.b)
end
-- Calculates the contrast ratio between the two given colors
function _color.contrast_ratio(fg, bg)
return (_color.relative_luminance(fg) + 0.05) / (_color.relative_luminance(bg) + 0.05)
end
-- Returns true if the contrast between the two given colors is suitable
function _color.is_contrast_acceptable(fg, bg)
return _color.contrast_ratio(fg, bg) >= 7 and true
end
-- Returns a bright-ish, saturated-ish, color of random hue
function _color.rand_hex(lb_angle, ub_angle)
return color_libary.color({
h = random(lb_angle or 0, ub_angle or 360),
s = 70,
v = 90,
}).hex
end
-- Rotates the hue of the given hex color by the specified angle (in degrees)
function _color.rotate_hue(color, angle)
color = color_libary.color({ hex = color })
angle = clip(angle or 0, 0, 360)
color.h = (color.h + angle) % 360
return color.hex
end
function _color.button_color(color, amount)
color = color_libary.color({ hex = color })
if _color.is_dark(color.hex) then
color = color + string.format("%fl", amount)
else
color = color - string.format("%fl", amount)
end
return color.hex
end
function _color.lighten(color, amount)
amount = amount or 0
color = color_libary.color({ hex = color })
color.l = color.l + amount
return color.hex
end
function _color.darken(color, amount)
amount = amount or 0
color = color_libary.color({ hex = color })
color.l = color.l - amount
return color.hex
end
-- Pywal like functions
function _color.pywal_blend(color1, color2)
color1 = color_libary.color({ hex = color1 })
color2 = color_libary.color({ hex = color2 })
return color_libary.color({
r = round(0.5 * color1.r + 0.5 * color2.r),
g = round(0.5 * color1.g + 0.5 * color2.g),
b = round(0.5 * color1.b + 0.5 * color2.b),
}).hex
end
function _color.pywal_saturate_color(color, amount)
color = color_libary.color({ hex = color })
color.s = clip(amount, 0, 1)
return color.hex
end
function _color.pywal_alter_brightness(color, amount, sat)
sat = sat or 0
color = color_libary.color({ hex = color })
color.l = clip(color.l + amount, 0, 1)
color.s = clip(color.s + sat, 0, 1)
return color.hex
end
function _color.pywal_lighten(color, amount)
color = color_libary.color({ hex = color })
color.r = round(color.r + (255 - color.r) * amount)
color.g = round(color.g + (255 - color.g) * amount)
color.b = round(color.b + (255 - color.b) * amount)
return color.hex
end
function _color.pywal_darken(color, amount)
color = color_libary.color({ hex = color })
color.r = round(color.r * (1 - amount))
color.g = round(color.g * (1 - amount))
color.b = round(color.b * (1 - amount))
return color.hex
end
return _color

View File

@ -0,0 +1,426 @@
local lgi = require("lgi")
local Gio = lgi.Gio
local Glib = lgi.GLib
local awful = require("awful")
local gtimer = require("gears.timer")
local tonumber = tonumber
local tostring = tostring
local ipairs = ipairs
local math = math
local os = os
local capi = { awesome = awesome }
local _filesystem = {}
function _filesystem.is_directory_readable_block(path)
local gfile = Gio.File.new_for_path(path)
local gfileinfo = gfile:query_info(
"standard::type,access::can-read,time::modified",
Gio.FileQueryInfoFlags.NONE
)
return gfileinfo and gfileinfo:get_file_type() == "DIRECTORY" and
gfileinfo:get_attribute_boolean("access::can-read")
end
function _filesystem.is_file_readable_block(path)
local gfile = Gio.File.new_for_path(path)
local gfileinfo = gfile:query_info(
"standard::type,access::can-read,time::modified",
Gio.FileQueryInfoFlags.NONE
)
return gfileinfo and gfileinfo:get_file_type() ~= "DIRECTORY" and
gfileinfo:get_attribute_boolean("access::can-read")
end
function _filesystem.read_file_block(path)
if _filesystem.is_file_readable_block(path) == false then
print("file '" .. path .. "' is not found or not readable...")
return nil
else
local gfile = Gio.File.new_for_path(path)
local content = gfile:load_contents()
if content == nil or content == false then
print("Failed reading " .. path)
return nil
else
return content
end
end
end
function _filesystem.query_info(path, callback)
local gfile = Gio.File.new_for_path(path)
gfile:query_info_async(
"standard::type,access::can-read,time::modified",
Gio.FileQueryInfoFlags.NONE, Glib.PRIORITY_DEFAULT,
nil,
function(_, info_result)
local info, error = gfile:query_info_finish(info_result)
if info == nil or error ~= nil then
callback(nil)
print(error)
return
else
callback(info)
end
end, nil)
end
function _filesystem.is_directory_readable(path, callback)
_filesystem.query_info(path, function(info)
if info ~= nil then
if info:get_file_type() == "DIRECTORY" and
info:get_attribute_boolean("access::can-read")
then
callback(true)
else
print("directory '" .. path .. "' is not found or not readable...")
callback(false)
end
else
print("info for directory '" .. path .. "' could not be retrived")
callback(false)
end
end)
end
function _filesystem.is_file_readable(path, callback)
_filesystem.query_info(path, function(info)
if info ~= nil then
if info:get_file_type() ~= "DIRECTORY" and
info:get_attribute_boolean("access::can-read")
then
callback(true)
else
print("file '" .. path .. "' is not found or not readable...")
callback(false)
end
else
print("info for file '" .. path .. "' could not be retrived")
callback(false)
end
end)
end
function _filesystem.make_directory(path, callback)
local gfile = Gio.File.new_for_path(path)
_filesystem.is_directory_readable(path, function(is_readable)
if is_readable then
print("directory '" .. path .. "' already exists")
callback(true)
return
else
gfile:make_directory_async(Glib.PRIORITY_DEFAULT, nil, function(file, task, c)
local result, error = gfile:make_directory_finish(task)
if result == false or error ~= nil then
print("Failed creating " .. path)
callback(false)
else
print("Successfully created " .. path)
callback(true)
end
end)
end
end)
end
function _filesystem.save_file(path, text, callback, is_retry)
print("writing to file " .. path)
local gfile = Gio.File.new_for_path(path)
_filesystem.is_file_readable(path, function(is_readable)
if not is_readable then
if is_retry then
print("failed creating file "..path)
if callback then
callback(false)
end
return
end
print("making parent directories...")
gfile:get_parent():make_directory_with_parents()
gfile:create_readwrite_async(Gio.FileCreateFlags.NONE, Glib.PRIORITY_DEFAULT, nil, function(_, create_result)
print("file created " .. tostring(gfile:create_readwrite_finish(create_result)))
_filesystem.save_file(path, text, callback, true)
end, nil)
else
gfile:open_readwrite_async(Glib.PRIORITY_DEFAULT, nil, function(_, io_stream_result)
local io_stream = gfile:open_readwrite_finish(io_stream_result)
io_stream:seek(0, Glib.SeekType.SET, nil)
local file = io_stream:get_output_stream()
file:write_all_async(text, Glib.PRIORITY_DEFAULT, nil, function(_, write_result)
local length_written = file:write_all_finish(write_result)
print("file written " .. length_written)
file:truncate(length_written, nil)
file:close_async(Glib.PRIORITY_DEFAULT, nil, function(_, file_close_result)
print("output stream closed " .. tostring(file:close_finish(file_close_result)))
io_stream:close_async(Glib.PRIORITY_DEFAULT, nil, function(_, stream_close_result)
print("file stream closed " .. tostring(io_stream:close_finish(stream_close_result)))
if callback then
callback(true)
end
end, nil)
end, nil)
end, nil)
end, nil)
end
end)
end
function _filesystem.read_file(path, callback)
local gfile = Gio.File.new_for_path(path)
_filesystem.is_file_readable(path, function(is_readable)
if not is_readable then
print("file '" .. path .. "' is not found or not readable...")
callback(nil)
else
gfile:load_contents_async(nil, function(file, task, c)
local content = gfile:load_contents_finish(task)
if content == nil then
print("Failed reading " .. path)
callback(nil)
else
callback(content)
end
end)
end
end)
end
function _filesystem.read_file_uri(uri, callback)
local gfile = Gio.File.new_for_uri(uri)
gfile:load_contents_async(nil, function(file, task, c)
local content = gfile:load_contents_finish(task)
if content == nil then
print("Failed reading " .. uri)
callback(nil)
else
callback(content)
end
end)
end
function _filesystem.delete_file(path, callback)
local gfile = Gio.File.new_for_path(path)
gfile:delete_async(Glib.PRIORITY_DEFAULT, nil, function(file, task, c)
local result, error = gfile:delete_finish(task)
if result == false or error ~= nil then
print("Failed deleting " .. tostring(error))
if callback then
callback(false)
end
else
if callback then
callback(true)
end
end
end)
end
function _filesystem.scan(path, callback, recursive)
if not path then
return
end
local result = {}
local function enumerator(path)
local gfile = Gio.File.new_for_path(path)
gfile:enumerate_children_async(
"standard::name,standard::type,access::can-read",
Gio.FileQueryInfoFlags.NONE,
0,
nil,
function(file, task, c)
local enum, error = file:enumerate_children_finish(task)
if enum == nil or error ~= nil then
print("Failed enumrating " .. path .. " " .. tostring(error))
callback(nil)
return
end
enum:next_files_async(99999, 0, nil, function(file_enum, task2, c)
local files, error = file_enum:next_files_finish(task2)
if files == nil or error ~= nil then
print("Failed enumrating " .. tostring(error))
callback(nil)
return
end
for _, file in ipairs(files) do
local file_child = enum:get_child(file)
local file_type = file:get_file_type()
local readable = file:get_attribute_boolean("access::can-read")
if file_type == "REGULAR" and readable then
local path = file_child:get_path()
if path ~= nil then
table.insert(result, path)
end
elseif file_type == "DIRECTORY" and recursive then
enumerator(file_child:get_path())
end
end
enum:close_async(0, nil)
callback(result)
end)
end)
end
enumerator(path)
end
function _filesystem.scan_with_folders(path, callback)
if not path then
return
end
local files_table = {}
local folders_table = {}
local function enumerator(path)
local gfile = Gio.File.new_for_path(path)
gfile:enumerate_children_async(
"standard::name,standard::type,access::can-read",
Gio.FileQueryInfoFlags.NONE,
0,
nil,
function(file, task, c)
local enum, error = file:enumerate_children_finish(task)
if enum == nil or error ~= nil then
print("Failed enumrating " .. path .. " " .. tostring(error))
callback(nil)
return
end
enum:next_files_async(99999, 0, nil, function(file_enum, task2, c)
local files, error = file_enum:next_files_finish(task2)
if files == nil or error ~= nil then
print("Failed enumrating " .. tostring(error))
callback(nil)
return
end
for _, file in ipairs(files) do
local file_child = enum:get_child(file)
local file_type = file:get_file_type()
local readable = file:get_attribute_boolean("access::can-read")
if file_type == "REGULAR" and readable then
local path = file_child:get_path()
if path ~= nil then
table.insert(files_table, path)
end
elseif file_type == "DIRECTORY" then
table.insert(folders_table, file_child:get_path())
end
end
enum:close_async(0, nil)
callback(files_table, folders_table)
end)
end)
end
enumerator(path)
end
function _filesystem.save_uri(path, uri, callback)
_filesystem.read_file_uri(uri, function(content)
if content == nil then
print("Failed to download file " .. uri)
callback(false)
else
_filesystem.save_file(path, content, function(result)
if result == true then
callback(true)
else
print("Failed to save " .. uri .. " to" .. path)
callback(false)
end
end)
end
end)
end
function _filesystem.remote_watch(path, uri, interval, callback, old_content_callback)
local function download()
_filesystem.read_file_uri(uri, function(content)
callback(content)
if content ~= nil and content ~= false then
_filesystem.read_file(path, function(old_content)
if old_content ~= nil and old_content ~= false then
if old_content_callback ~= nil then
old_content_callback(old_content)
end
end
_filesystem.save_file(path, content)
end)
end
end)
end
_filesystem.read_file(path, function(old_content)
if old_content ~= nil and old_content ~= false then
if old_content_callback ~= nil then
old_content_callback(old_content)
end
end
local timer
timer = gtimer
{
timeout = interval,
call_now = true,
autostart = true,
single_shot = false,
callback = function()
_filesystem.query_info(path, function(info)
if info ~= nil then
local time = info:get_modification_date_time()
local diff = math.ceil(Glib.DateTime.new_now_local():difference(time) / 1000000)
if diff >= interval then
print("Enough time has passed, redownloading " .. path)
download()
else
_filesystem.read_file(path, function(content)
if content == nil or content:gsub("%s+", "") == "" then
print("Empty file, Redownloading " .. path)
download()
else
callback(content)
end
end)
-- Schedule an update for when the remaining time to complete the interval passes
timer:stop()
gtimer.start_new(interval - diff, function()
print("Finally! redownloading " .. path)
download()
timer:again()
end)
end
else
print(path .. " doesn't exist, downloading " .. uri)
download()
end
end)
end
}
end)
end
function _filesystem.get_awesome_config_dir(sub_folder)
return (capi.awesome.conffile:match(".*/") or "./") .. sub_folder .. "/"
end
function _filesystem.get_cache_dir(sub_folder)
return (os.getenv("XDG_CACHE_HOME") or os.getenv("HOME") .. "/.cache")
.. "/awesome/" .. sub_folder .. "/"
end
function _filesystem.get_xdg_cache_home(sub_folder)
return (os.getenv("XDG_CACHE_HOME") or os.getenv("HOME") .. "/.cache")
.. "/" .. sub_folder .. "/"
end
return _filesystem

View File

@ -0,0 +1,8 @@
return {
client = require(... .. ".client"),
color = require(... .. ".color"),
filesystem = require(... .. ".filesystem"),
misc = require(... .. ".misc"),
run = require(... .. ".run"),
ui = require(... .. ".ui"),
}

105
config/awesome/helpers/misc.lua Executable file
View File

@ -0,0 +1,105 @@
local awful = require("awful")
local naughty = require("naughty")
local gears = require("gears")
local beautiful = require("beautiful")
local icons = require("icons")
local math = math
local os = os
local capi = { awesome = awesome, client = client }
local _misc = {}
-- Send key
function _misc.send_key(c, key)
awful.spawn.with_shell("xdotool key --window " .. tostring(c.window) .. " " .. key)
end
--- Converts string representation of date (2020-06-02T11:25:27Z) to date
function _misc.parse_date(date_str)
local pattern = "(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)%Z"
local y, m, d, h, min, sec, _ = date_str:match(pattern)
return os.time({ year = y, month = m, day = d, hour = h, min = min, sec = sec })
end
--- Converts seconds to "time ago" representation, like '1 hour ago'
function _misc.to_time_ago(seconds)
local days = seconds / 86400
if days >= 1 then
days = math.floor(days)
return days .. (days == 1 and " day" or " days") .. " ago"
end
local hours = (seconds % 86400) / 3600
if hours >= 1 then
hours = math.floor(hours)
return hours .. (hours == 1 and " hour" or " hours") .. " ago"
end
local minutes = ((seconds % 86400) % 3600) / 60
if minutes >= 1 then
minutes = math.floor(minutes)
return minutes .. (minutes == 1 and " minute" or " minutes") .. " ago"
end
return "Now"
end
function _misc.tag_back_and_forth(tag_index)
local s = awful.screen.focused()
local tag = s.tags[tag_index]
if tag then
if tag == s.selected_tag then
awful.tag.history.restore()
else
tag:view_only()
end
local urgent_clients = function(c)
return awful.rules.match(c, { urgent = true, first_tag = tag })
end
for c in awful.client.iterate(urgent_clients) do
capi.client.focus = c
c:raise()
end
end
end
function _misc.prompt(action, textbox, prompt, callback)
if action == "run" then
awful.prompt.run({
prompt = prompt,
-- prompt = "<b>Run: </b>",
textbox = textbox,
font = beautiful.font_name .. "Regular 12",
done_callback = callback,
exe_callback = awful.spawn,
completion_callback = awful.completion.shell,
history_path = awful.util.get_cache_dir() .. "/history",
})
elseif action == "web_search" then
awful.prompt.run({
prompt = prompt,
-- prompt = '<b>Web search: </b>',
textbox = textbox,
font = beautiful.font_name .. "Regular 12",
history_path = awful.util.get_cache_dir() .. "/history_web",
done_callback = callback,
exe_callback = function(input)
if not input or #input == 0 then
return
end
awful.spawn.with_shell("noglob " .. "xdg-open https://www.google.com/search?q=" .. "'" .. input .. "'")
naughty.notify({
title = "Searching the web for",
text = input,
icon = gears.color.recolor_image(icons.web_browser, beautiful.accent),
urgency = "low",
})
end,
})
end
end
return _misc

50
config/awesome/helpers/run.lua Executable file
View File

@ -0,0 +1,50 @@
local awful = require("awful")
local tostring = tostring
local string = string
local ipairs = ipairs
local math = math
local os = os
local _run = {}
function _run.run_once_pgrep(cmd)
local findme = cmd
local firstspace = cmd:find(" ")
if firstspace then
findme = cmd:sub(0, firstspace - 1)
end
awful.spawn.easy_async_with_shell(string.format("pgrep -u $USER -x %s > /dev/null || (%s)", findme, cmd))
end
function _run.run_once_ps(findme, cmd)
awful.spawn.easy_async_with_shell(string.format("ps -C %s|wc -l", findme), function(stdout)
if tonumber(stdout) ~= 2 then
awful.spawn(cmd, false)
end
end)
end
function _run.run_once_grep(command)
awful.spawn.easy_async_with_shell(string.format("ps aux | grep '%s' | grep -v 'grep'", command), function(stdout)
if stdout == "" or stdout == nil then
awful.spawn(command, false)
end
end)
end
function _run.check_if_running(command, running_callback, not_running_callback)
awful.spawn.easy_async_with_shell(string.format("ps aux | grep '%s' | grep -v 'grep'", command), function(stdout)
if stdout == "" or stdout == nil then
if not_running_callback ~= nil then
not_running_callback()
end
else
if running_callback ~= nil then
running_callback()
end
end
end)
end
return _run

113
config/awesome/helpers/ui.lua Executable file
View File

@ -0,0 +1,113 @@
local awful = require("awful")
local wibox = require("wibox")
local gshape = require("gears.shape")
local gmatrix = require("gears.matrix")
local ipairs = ipairs
local table = table
local capi = { mouse = mouse }
local _ui = {}
function _ui.colorize_text(text, color)
return "<span foreground='" .. color .. "'>" .. text .. "</span>"
end
function _ui.add_hover_cursor(w, hover_cursor)
local original_cursor = "left_ptr"
w:connect_signal("mouse::enter", function()
local widget = capi.mouse.current_wibox
if widget then
widget.cursor = hover_cursor
end
end)
w:connect_signal("mouse::leave", function()
local widget = capi.mouse.current_wibox
if widget then
widget.cursor = original_cursor
end
end)
end
function _ui.vertical_pad(height)
return wibox.widget({
forced_height = height,
layout = wibox.layout.fixed.vertical,
})
end
function _ui.horizontal_pad(width)
return wibox.widget({
forced_width = width,
layout = wibox.layout.fixed.horizontal,
})
end
function _ui.rrect(radius)
return function(cr, width, height)
gshape.rounded_rect(cr, width, height, radius)
end
end
function _ui.pie(width, height, start_angle, end_angle, radius)
return function(cr)
gshape.pie(cr, width, height, start_angle, end_angle, radius)
end
end
function _ui.prgram(height, base)
return function(cr, width)
gshape.parallelogram(cr, width, height, base)
end
end
function _ui.prrect(radius, tl, tr, br, bl)
return function(cr, width, height)
gshape.partially_rounded_rect(cr, width, height, tl, tr, br, bl, radius)
end
end
function _ui.custom_shape(cr, width, height)
cr:move_to(0, height / 25)
cr:line_to(height / 25, 0)
cr:line_to(width, 0)
cr:line_to(width, height - height / 25)
cr:line_to(width - height / 25, height)
cr:line_to(0, height)
cr:close_path()
end
local function _get_widget_geometry(_hierarchy, widget)
local width, height = _hierarchy:get_size()
if _hierarchy:get_widget() == widget then
-- Get the extents of this widget in the device space
local x, y, w, h = gmatrix.transform_rectangle(_hierarchy:get_matrix_to_device(), 0, 0, width, height)
return { x = x, y = y, width = w, height = h, hierarchy = _hierarchy }
end
for _, child in ipairs(_hierarchy:get_children()) do
local ret = _get_widget_geometry(child, widget)
if ret then
return ret
end
end
end
function _ui.get_widget_geometry(wibox, widget)
return _get_widget_geometry(wibox._drawable._widget_hierarchy, widget)
end
function _ui.screen_mask(s, bg)
local mask = wibox({
visible = false,
ontop = true,
type = "splash",
screen = s,
})
awful.placement.maximize(mask)
mask.bg = bg
return mask
end
return _ui

View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="520"
height="64"
id="svg2"
sodipodi:docname="Awesome_logo_horizontal3.svg"
inkscape:version="0.92.0 r">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1062"
id="namedview42"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.3100951"
inkscape:cx="208.7345"
inkscape:cy="-24.8331"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<metadata
id="metadata8">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs6" />
<path
d="M 0,63.999999 V 22 H 43.999999 V 20 H 0 V 0 h 63.999999 v 63.999999 h -20 V 42 H 20 v 2 h 21.999999 v 19.999999 z"
id="path10"
style="fill:#535d6c"
inkscape:connector-curvature="0" />
<path
style="fill:#535d6c"
id="path5121"
d="M 75.999999,0 H 118 v 43.999999 h 2 V 0 h 20 V 63.999999 H 75.999999 c 0.03867,-19.975266 1.21505,-20 22,-20 V 0 l -2,3.13e-4 v 44.03356 l -20,19.966102 z"
inkscape:connector-curvature="0" />
<path
d="m 216,0 v 42 h -44 v 2 h 44 V 64 H 152 V 0 c 19.97527,0.03867 20,1.21505 20,22 h 44 l -3.1e-4,-2 H 171.96613 L 152.00002,0 Z"
id="path5123"
style="fill:#535d6c"
inkscape:connector-curvature="0" />
<path
style="fill:#535d6c"
id="path5125"
d="M 292,63.999999 V 22 h -44 v -2 h 44 V 0 h -64 v 42.011827 l 20,-0.01183 v 0 h 24 v 2.09375 h -44 l -0.009,19.906249 z"
inkscape:connector-curvature="0" />
<path
d="m 324.3125,63.999999 c 0,-43.882671 0.18277,-0.110242 -0.0915,-43.999999 h 0.88388 42.89515 V 0 h -64 c 0,63.768424 -0.009,0.109806 -0.009,63.999996 z"
id="path5127"
style="fill:#535d6c"
inkscape:connector-curvature="0" />
<path
d="m 380,63.999999 h 42 V 20.000001 h 2 v 43.999998 h 20 V 0 h -64 c 0.0387,19.975267 1.21505,20.000001 22,20.000001 v 43.999998 l -2,-3.13e-4 V 19.966127 L 380,2.4e-5 Z"
id="path5129"
style="fill:#535d6c"
inkscape:connector-curvature="0" />
<path
style="fill:#535d6c"
id="path5131"
d="m 520,0 v 42 h -44 v 2 h 44 V 64 H 456 V 0 c 19.97527,0.03867 20,1.21505 20,22 h 44 l -3.1e-4,-2 H 475.96613 L 456.00002,0 Z"
inkscape:connector-curvature="0" />
<path
style="fill:#535d6c"
id="path5135"
d="m 332.34911,63.999999 c 0,-44.007671 -0.003,-0.02174 -0.003,-43.999999 0,0 30.24668,0 35.65403,0 V 0 h -64 c 0,63.768424 -0.009,0.109806 -0.009,63.999996 z"
inkscape:connector-curvature="0" />
<path
d="m 334.99382,63.999999 c 0,0 -0.003,-0.02174 -0.003,-43.999999 h 32.71653 V 0 h -63.70721 c 0,63.768424 -0.009,0.109806 -0.009,63.999996 z"
id="path5137"
style="fill:#535d6c"
inkscape:connector-curvature="0" />
<path
style="fill:#535d6c"
id="path5162"
d="m 347.67867,0 c 0,43.882671 -0.18277,0.110242 0.0915,43.999999 h -0.88388 -42.89515 v 20 h 64 c 0,-63.768424 0.009,-0.109806 0.009,-63.999996 z"
inkscape:connector-curvature="0" />
<path
d="m 339.64206,0 c 0,44.007671 0.003,0.02174 0.003,43.999999 0,0 -30.24668,0 -35.65403,0 v 20 h 64 c 0,-63.768424 0.009,-0.109806 0.009,-63.999996 z"
id="path5164"
style="fill:#535d6c"
inkscape:connector-curvature="0" />
<path
style="fill:#535d6c"
id="path5166"
d="m 336.99735,0 c 0,0 0.003,0.02174 0.003,43.999999 h -32.71653 v 20 h 63.70721 c 0,-63.768424 0.009,-0.109806 0.009,-63.999996 z"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M96 320h48V192H96V320zM544 192V160c0-35.35-28.65-64-64-64H64C28.65 96 0 124.7 0 160v192c0 35.35 28.65 64 64 64h416c35.35 0 64-28.65 64-64v-32c17.67 0 32-14.33 32-32V224C576 206.3 561.7 192 544 192zM496 352c0 8.822-7.178 16-16 16H64c-8.822 0-16-7.178-16-16V160c0-8.822 7.178-16 16-16h416c8.822 0 16 7.178 16 16V352z"/></svg>

After

Width:  |  Height:  |  Size: 538 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M96 320h352V192H96V320zM544 192V160c0-35.35-28.65-64-64-64H64C28.65 96 0 124.7 0 160v192c0 35.35 28.65 64 64 64h416c35.35 0 64-28.65 64-64v-32c17.67 0 32-14.33 32-32V224C576 206.3 561.7 192 544 192zM496 352c0 8.822-7.178 16-16 16H64c-8.822 0-16-7.178-16-16V160c0-8.822 7.178-16 16-16h416c8.822 0 16 7.178 16 16V352z"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M48 352V160c0-8.822 7.178-16 16-16h106.6L212.9 96H64C28.65 96 0 124.7 0 160v192c0 35.35 28.65 64 64 64h73.87l17.29-48H64C55.18 368 48 360.8 48 352zM544 192V160c0-35.35-28.65-64-64-64h-73.87l-17.29 48H480c8.822 0 16 7.178 16 16v192c0 8.822-7.178 16-16 16h-106.6L331.1 416H480c35.35 0 64-28.65 64-64v-32c17.67 0 32-14.33 32-32V224C576 206.3 561.7 192 544 192zM397.9 246.1C393.1 237.5 385.4 232 376 232h-69.84l60.44-167.9c3.906-10.84-.4687-22.92-10.38-28.78c-9.906-5.844-22.59-3.875-30.25 4.797l-176 200C143.8 247.2 142.3 257.3 146.1 265.9C149.1 274.5 158.6 280 168 280h69.84l-60.44 167.9c-3.906 10.84 .4687 22.92 10.38 28.78C191.6 478.9 195.8 480 200 480c6.688 0 13.31-2.812 18.03-8.141l176-200C400.3 264.8 401.8 254.7 397.9 246.1z"/></svg>

After

Width:  |  Height:  |  Size: 953 B

View File

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M130.22 127.548C130.38 127.558 130.3 127.558 130.22 127.548V127.548ZM481.64 172.898C471.03 147.398 449.56 119.898 432.7 111.168C446.42 138.058 454.37 165.048 457.4 185.168C457.405 185.306 457.422 185.443 457.45 185.578C429.87 116.828 383.098 89.1089 344.9 28.7479C329.908 5.05792 333.976 3.51792 331.82 4.08792L331.7 4.15792C284.99 30.1109 256.365 82.5289 249.12 126.898C232.503 127.771 216.219 131.895 201.19 139.035C199.838 139.649 198.736 140.706 198.066 142.031C197.396 143.356 197.199 144.87 197.506 146.323C197.7 147.162 198.068 147.951 198.586 148.639C199.103 149.327 199.76 149.899 200.512 150.318C201.264 150.737 202.096 150.993 202.954 151.071C203.811 151.148 204.676 151.045 205.491 150.768L206.011 150.558C221.511 143.255 238.408 139.393 255.541 139.238C318.369 138.669 352.698 183.262 363.161 201.528C350.161 192.378 326.811 183.338 304.341 187.248C392.081 231.108 368.541 381.784 246.951 376.448C187.487 373.838 149.881 325.467 146.421 285.648C146.421 285.648 157.671 243.698 227.041 243.698C234.541 243.698 255.971 222.778 256.371 216.698C256.281 214.698 213.836 197.822 197.281 181.518C188.434 172.805 184.229 168.611 180.511 165.458C178.499 163.75 176.392 162.158 174.201 160.688C168.638 141.231 168.399 120.638 173.51 101.058C148.45 112.468 128.96 130.508 114.8 146.428H114.68C105.01 134.178 105.68 93.7779 106.25 85.3479C106.13 84.8179 99.022 89.0159 98.1 89.6579C89.5342 95.7103 81.5528 102.55 74.26 110.088C57.969 126.688 30.128 160.242 18.76 211.318C14.224 231.701 12 255.739 12 263.618C12 398.318 121.21 507.508 255.92 507.508C376.56 507.508 478.939 420.281 496.35 304.888C507.922 228.192 481.64 173.82 481.64 172.898Z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

20
config/awesome/icons/init.lua Executable file
View File

@ -0,0 +1,20 @@
--- Icons directory
local gfs = require("gears.filesystem")
local dir = gfs.get_configuration_dir() .. "icons/"
return {
--- notifications
notification = dir .. "notification.svg",
notification_bell = dir .. "notification_bell.svg",
--- system UI
ram = dir .. "ram.svg",
cpu = dir .. "cpu.svg",
temp = dir .. "temp.svg",
disk = dir .. "disk.svg",
battery = dir .. "battery.svg",
battery_low = dir .. "battery-low.svg",
charging = dir .. "charging.svg",
web_browser = dir .. "firefox.svg",
awesome_logo = dir .. "awesome-logo.svg",
}

View File

Before

Width:  |  Height:  |  Size: 398 B

After

Width:  |  Height:  |  Size: 398 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -1,134 +0,0 @@
local Gio = require("lgi").Gio
local Gtk = require("lgi").Gtk
local gobject = require("gears.object")
local gtable = require("gears.table")
local helpers = require("helpers")
local setmetatable = setmetatable
local ipairs = ipairs
local icon_theme = { mt = {} }
function icon_theme:get_client_icon_path(client)
local function find_icon(class)
if self._private.client_icon_cache[class] ~= nil then
return self._private.client_icon_cache[class]
end
for _, app in ipairs(Gio.AppInfo.get_all()) do
local id = Gio.AppInfo.get_id(app)
if id:match(helpers.misc.case_insensitive_pattern(class)) then
self._private.client_icon_cache[class] = self:get_gicon_path(Gio.AppInfo.get_icon(app))
return self._private.client_icon_cache[class]
end
end
return nil
end
local class = client.class
if class == "jetbrains-studio" then
class = "android-studio"
end
local icon = self:get_icon_path("gnome-window-manager")
if class ~= nil then
class = class:gsub("[%-]", "%%%0")
icon = find_icon(class) or icon
class = client.class
class = class:gsub("[%-]", "")
icon = find_icon(class) or icon
class = client.class
class = class:gsub("[%-]", ".")
icon = find_icon(class) or icon
class = client.class
class = class:match("(.-)-") or class
class = class:match("(.-)%.") or class
class = class:match("(.-)%s+") or class
class = class:gsub("[%-]", "%%%0")
icon = find_icon(class) or icon
end
return icon
end
function icon_theme:choose_icon(icons_names)
local icon_info = Gtk.IconTheme.choose_icon(self.gtk_theme, icons_names, self.icon_size, 0);
if icon_info then
local icon_path = Gtk.IconInfo.get_filename(icon_info)
if icon_path then
return icon_path
end
end
return ""
end
function icon_theme:get_gicon_path(gicon)
if gicon == nil then
return ""
end
if self._private.icon_cache[gicon] ~= nil then
return self._private.icon_cache[gicon]
end
local icon_info = Gtk.IconTheme.lookup_by_gicon(self.gtk_theme, gicon, self.icon_size, 0);
if icon_info then
local icon_path = Gtk.IconInfo.get_filename(icon_info)
if icon_path then
self._private.icon_cache[gicon] = icon_path
return icon_path
end
end
return ""
end
function icon_theme:get_icon_path(icon_name)
if self._private.icon_cache[icon_name] ~= nil then
return self._private.icon_cache[icon_name]
end
local icon_info = Gtk.IconTheme.lookup_icon(self.gtk_theme, icon_name, self.icon_size, 0);
if icon_info then
local icon_path = Gtk.IconInfo.get_filename(icon_info)
if icon_path then
self._private.icon_cache[icon_name] = icon_path
return icon_path
end
end
return ""
end
local function new(theme_name, icon_size)
local ret = gobject{}
gtable.crush(ret, icon_theme, true)
ret._private = {}
ret._private.client_icon_cache = {}
ret._private.icon_cache = {}
ret.name = theme_name or nil
ret.icon_size = icon_size or 48
if theme_name then
ret.gtk_theme = Gtk.IconTheme.new()
Gtk.IconTheme.set_custom_theme(ret.gtk_theme, theme_name);
else
ret.gtk_theme = Gtk.IconTheme.get_default()
end
return ret
end
function icon_theme.mt:__call(...)
return new(...)
end
return setmetatable(icon_theme, icon_theme.mt)

View File

@ -1,87 +0,0 @@
local gears = require("gears")
--typesafe function overloader (copied from lua-users.org) {{{
--source: http://lua-users.org/wiki/OverloadedFunctions
local function overloaded()
local fns = {}
local mt = {}
local function oerror()
return error("Invalid argument types to overloaded function")
end
function mt:__call(...)
local arg = {...}
local default = self.default
local signature = {}
for i,arg in ipairs {...} do
signature[i] = type(arg)
end
signature = table.concat(signature, ",")
return (fns[signature] or self.default)(...)
end
function mt:__index(key)
local signature = {}
local function __newindex(self, key, value)
print(key, type(key), value, type(value))
signature[#signature+1] = key
fns[table.concat(signature, ",")] = value
print("bind", table.concat(signature, ", "))
end
local function __index(self, key)
print("I", key, type(key))
signature[#signature+1] = key
return setmetatable({}, { __index = __index, __newindex = __newindex })
end
return __index(self, key)
end
function mt:__newindex(key, value)
fns[key] = value
end
return setmetatable({ default = oerror }, mt)
end
--}}}
local function dec_hex(IN)
local B,K,OUT,I,D=16,"0123456789ABCDEF","",0
while IN>0 do
I=I+1
IN,D=math.floor(IN/B),(IN%B)+1
OUT=string.sub(K,D,D)..OUT
end
return #OUT == 2 and OUT or "0" .. OUT
end
-- color helpers {{{
local color = {}
color.col_shift = overloaded()
color.col_shift.string.number = function(c, s)
local r,g,b,o = gears.color.parse_color(c)
return "#" .. dec_hex(r*255+s)
.. dec_hex(g*255+s)
.. dec_hex(b*255+s)
.. dec_hex(o*255)
end
color.col_shift.string.number.number.number = function(c,sr,sg,sb)
local r,g,b,o = gears.color.parse_color(c)
return "#" .. dec_hex(r*255+sr)
.. dec_hex(g*255+sg)
.. dec_hex(b*255+sb)
.. dec_hex(o*255)
end
color.col_shift.string.number.number.number.number = function(c,sr,sg,sb,so)
local r,g,b,o = gears.color.parse_color(c)
return "#" .. dec_hex(r*255+sr)
.. dec_hex(g*255+sg)
.. dec_hex(b*255+sb)
.. dec_hex(o*255+so)
end
color.col_diff = function(f, s)
local fr, fg, fb, fo = gears.color.parse_color(f)
local sr, sg, sb, so = gears.color.parse_color(s)
return sr-fr,sg-fg,sb-fb,so-fo
end
--}}}
return {
color = color
}

View File

@ -1,12 +0,0 @@
local gears = require("gears")
local dock = require("module.dock")
dock.init(screen.primary, 50, 5, gears.shape.rounded_rect)
require("module.bling")
require("module.rubato")
require("module.layout-machi")
require("module.better-resize")
require("module.exit-screen")
require("module.tooltip")
require("module.savefloats")
require("module.window_switcher").enable()

View File

@ -1,226 +0,0 @@
local gears = require("gears")
local awful = require("awful")
local beautiful = require("beautiful")
local wibox = require("wibox")
local helpers = require("helpers")
local function create_boxed_widget(widget_to_be_boxed, width, height, inner_pad)
local box_container = wibox.container.background()
box_container.bg = beautiful.tooltip_widget_bg
box_container.forced_height = height
box_container.forced_width = width
box_container.shape = helpers.rrect(beautiful.tooltip_box_border_radius)
local inner = dpi(0)
if inner_pad then
inner = beautiful.tooltip_box_margin
end
local boxed_widget = wibox.widget({
-- Add margins
{
-- Add background color
{
-- The actual widget goes here
widget_to_be_boxed,
margins = inner,
widget = wibox.container.margin,
},
widget = box_container,
},
margins = beautiful.tooltip_gap / 2,
color = "#FF000000",
widget = wibox.container.margin,
})
return boxed_widget
end
-- Tooltip widgets
---------------------
awful.screen.connect_for_each_screen(function(s)
-- Battery
-------------
local cute_battery_face = require("ui.widgets.cute-battery-face")
-- Date
----------
local date_day = wibox.widget({
font = beautiful.font_name .. "bold 10",
format = helpers.colorize_text("%A", beautiful.xforeground),
align = "center",
valign = "center",
widget = wibox.widget.textclock,
})
local date_month = wibox.widget({
font = beautiful.font_name .. "bold 14",
format = "%d %B %Y",
align = "center",
valign = "center",
widget = wibox.widget.textclock,
})
local date = wibox.widget({
date_day,
nil,
date_month,
layout = wibox.layout.align.vertical,
})
-- Separator
---------------
local separator = wibox.widget({
{
bg = beautiful.accent,
shape = helpers.rrect(dpi(5)),
forced_width = dpi(3),
widget = wibox.container.background,
},
right = dpi(5),
widget = wibox.container.margin,
})
-- Analog clock
------------------
local analog_clock = require("ui.widgets.analog-clock")
-- Wifi
----------
local wifi_status_icon = wibox.widget({
markup = "󰤫",
font = beautiful.icon_font_name .. "14",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
})
local wifi = wibox.widget({
wifi_status_icon,
forced_width = dpi(30),
forced_height = dpi(30),
bg = beautiful.tooltip_bg,
shape = gears.shape.circle,
widget = wibox.container.background,
})
local wifi_status = false
awesome.connect_signal("signal::network", function(status, ssid)
wifi_status = status
awesome.emit_signal("widget::network")
end)
awesome.connect_signal("widget::network", function()
local w, fill_color
if wifi_status == true then
w = "󰤨"
fill_color = beautiful.xcolor2
else
w = "󰤭"
fill_color = beautiful.xcolor1
end
wifi.shape_border_color = fill_color
wifi_status_icon.markup = helpers.colorize_text(w, fill_color)
end)
-- UpTime
------------
local uptime_label = wibox.widget({
font = beautiful.font_name .. "medium 9",
markup = helpers.colorize_text("Uptime", beautiful.accent),
valign = "center",
widget = wibox.widget.textbox,
})
local uptime_text = wibox.widget({
font = beautiful.font_name .. "bold 13",
markup = helpers.colorize_text("-", beautiful.accent),
valign = "center",
widget = wibox.widget.textbox,
})
awesome.connect_signal("signal::uptime", function(uptime_value)
uptime_text.markup = uptime_value
end)
local uptime_container = wibox.widget({
separator,
{
uptime_label,
nil,
uptime_text,
layout = wibox.layout.align.vertical,
},
{
wifi,
layout = wibox.layout.align.vertical,
},
layout = wibox.layout.align.horizontal,
})
-- Widget
------------
local uptime_boxed = create_boxed_widget(uptime_container, dpi(170), dpi(50), true)
local analog_clock_boxed = create_boxed_widget(analog_clock, dpi(110), dpi(110), true)
-- Tooltip setup
-------------------
s.stats_tooltip = wibox({
type = "dock",
screen = s,
height = beautiful.tooltip_height,
width = beautiful.tooltip_width,
bg = beautiful.transparent,
ontop = true,
visible = false,
})
awful.placement.top_right(s.stats_tooltip, {
margins = {
top = beautiful.useless_gap * 16,
bottom = beautiful.useless_gap * 6,
left = beautiful.useless_gap * 6,
right = beautiful.useless_gap * 6,
},
})
s.stats_tooltip:setup({
{
{
{
{
date,
{
analog_clock_boxed,
nil,
cute_battery_face,
expand = "none",
layout = wibox.layout.fixed.horizontal,
},
layout = wibox.layout.fixed.vertical,
},
layout = wibox.layout.fixed.horizontal,
},
{
uptime_boxed,
layout = wibox.layout.fixed.horizontal,
},
layout = wibox.layout.fixed.vertical,
},
margins = beautiful.tooltip_gap,
widget = wibox.container.margin,
},
shape = helpers.rrect(beautiful.tooltip_border_radius),
bg = beautiful.tooltip_bg,
widget = wibox.container.background,
})
end)
function tooltip_toggle()
local s = awful.screen.focused()
s.stats_tooltip.visible = not s.stats_tooltip.visible
end

View File

@ -0,0 +1,232 @@
-------------------------------------------
-- @author https://github.com/Kasper24
-- @copyright 2021-2022 Kasper24
-------------------------------------------
local GLib = require("lgi").GLib
local gobject = require("gears.object")
local gtable = require("gears.table")
local subscribable = require("modules.animation.subscribable")
local tween = require("modules.animation.tween")
local ipairs = ipairs
local table = table
local pairs = pairs
local animation_manager = {}
animation_manager.easing = {
linear = "linear",
inQuad = "inQuad",
outQuad = "outQuad",
inOutQuad = "inOutQuad",
outInQuad = "outInQuad",
inCubic = "inCubic",
outCubic = "outCubic",
inOutCubic = "inOutCubic",
outInCubic = "outInCubic",
inQuart = "inQuart",
outQuart = "outQuart",
inOutQuart = "inOutQuart",
outInQuart = "outInQuart",
inQuint = "inQuint",
outQuint = "outQuint",
inOutQuint = "inOutQuint",
outInQuint = "outInQuint",
inSine = "inSine",
outSine = "outSine",
inOutSine = "inOutSine",
outInSine = "outInSine",
inExpo = "inExpo",
outExpo = "outExpo",
inOutExpo = "inOutExpo",
outInExpo = "outInExpo",
inCirc = "inCirc",
outCirc = "outCirc",
inOutCirc = "inOutCirc",
outInCirc = "outInCirc",
inElastic = "inElastic",
outElastic = "outElastic",
inOutElastic = "inOutElastic",
outInElastic = "outInElastic",
inBack = "inBack",
outBack = "outBack",
inOutBack = "inOutBack",
outInBack = "outInBack",
inBounce = "inBounce",
outBounce = "outBounce",
inOutBounce = "inOutBounce",
outInBounce = "outInBounce",
}
local animation = {}
local instance = nil
local ANIMATION_FRAME_DELAY = 5
local function micro_to_milli(micro)
return micro / 1000
end
local function second_to_micro(sec)
return sec * 1000000
end
local function second_to_milli(sec)
return sec * 1000
end
function animation:start(args)
args = args or {}
-- Awestoer/Rubbto compatibility
-- I'd rather this always be a table, but Awestoer/Rubbto
-- except the :set() method to have 1 number value parameter
-- used to set the target
local is_table = type(args) == "table"
local initial = is_table and (args.pos or self.pos) or self.pos
local subject = is_table and (args.subject or self.subject) or self.subject
local target = is_table and (args.target or self.target) or args
local duration = is_table and (args.duration or self.duration) or self.duration
local easing = is_table and (args.easing or self.easing) or self.easing
duration = self._private.anim_manager._private.instant == true and 0.01 or duration
if self.tween == nil or self.reset_on_stop == true then
self.tween = tween.new({
initial = initial,
subject = subject,
target = target,
duration = second_to_micro(duration),
easing = easing,
})
end
if self._private.anim_manager._private.animations[self.index] == nil then
table.insert(self._private.anim_manager._private.animations, self)
end
self.state = true
self.last_elapsed = GLib.get_monotonic_time()
self.started:fire()
self:emit_signal("started")
end
function animation:set(args)
self:start(args)
self:emit_signal("set")
end
function animation:stop()
self.state = false
self:emit_signal("stopped")
end
function animation:abort(reset)
animation:stop(reset)
self:emit_signal("aborted")
end
function animation:initial()
return self._private.initial
end
function animation_manager:set_instant(value)
self._private.instant = value
end
function animation_manager:new(args)
args = args or {}
args.pos = args.pos or 0
args.subject = args.subject or nil
args.target = args.target or nil
args.duration = args.duration or 0
args.easing = args.easing or nil
args.loop = args.loop or false
args.signals = args.signals or {}
args.update = args.update or nil
args.reset_on_stop = args.reset_on_stop == nil and true or args.reset_on_stop
-- Awestoer/Rubbto compatibility
args.subscribed = args.subscribed or nil
local ret = subscribable()
ret.started = subscribable()
ret.ended = subscribable()
if args.subscribed ~= nil then
ret:subscribe(args.subscribed)
end
for sig, sigfun in pairs(args.signals) do
ret:connect_signal(sig, sigfun)
end
if args.update ~= nil then
ret:connect_signal("update", args.update)
end
gtable.crush(ret, args, true)
gtable.crush(ret, animation, true)
ret._private = {}
ret._private.anim_manager = self
ret._private.initial = args.pos
return ret
end
local function new()
local ret = gobject({})
gtable.crush(ret, animation_manager, true)
ret._private = {}
ret._private.animations = {}
ret._private.instant = false
GLib.timeout_add(GLib.PRIORITY_DEFAULT, ANIMATION_FRAME_DELAY, function()
for index, animation in ipairs(ret._private.animations) do
if animation.state == true then
-- compute delta time
local time = GLib.get_monotonic_time()
local delta = time - animation.last_elapsed
animation.last_elapsed = time
-- If pos is true, the animation has ended
local pos = animation.tween:update(delta)
if pos == true then
-- Loop the animation, don't end it.
-- Useful for widgets like the spinning cicle
if animation.loop == true then
animation.tween:reset()
else
-- Snap to end
animation.pos = animation.tween.target
animation:fire(animation.pos)
animation:emit_signal("update", animation.pos)
animation.state = false
animation.ended:fire(pos)
table.remove(ret._private.animations, index)
animation:emit_signal("ended", animation.pos)
end
-- Animation in process, keep updating
else
animation.pos = pos
animation:fire(animation.pos)
animation:emit_signal("update", animation.pos)
end
else
table.remove(ret._private.animations, index)
end
end
-- call again the function after cooldown
return true
end)
return ret
end
if not instance then
instance = new()
end
return instance

View File

@ -0,0 +1,34 @@
local gobject = require("gears.object")
-- Kidna copying awesotre's stores on a surface level for added compatibility
local function subscribable(args)
local ret = gobject{}
local subscribed = {}
-- Subscrubes a function to the object so that it's called when `fire` is
-- Calls subscribe_callback if it exists as well
function ret:subscribe(func)
local id = tostring(func):gsub("function: ", "")
subscribed[id] = func
if self.subscribe_callback then self.subscribe_callback(func) end
end
-- Unsubscribes a function and calls unsubscribe_callback if it exists
function ret:unsubscribe(func)
if not func then
subscribed = {}
else
local id = tostring(func):gsub("function: ", "")
subscribed[id] = nil
end
if self.unsubscribe_callback then self.unsubscribe_callback(func) end
end
function ret:fire(...) for _, func in pairs(subscribed) do func(...) end end
return ret
end
return subscribable

View File

@ -0,0 +1,487 @@
-- easing
-- Adapted from https://github.com/EmmanuelOga/easing. See LICENSE.txt for credits.
-- For all easing functions:
-- t = time == how much time has to pass for the tweening to complete
-- b = begin == starting property value
-- c = change == ending - beginning
-- d = duration == running time. How much time has passed *right now*
local gobject = require("gears.object")
local gtable = require("gears.table")
local tween = {
_VERSION = 'tween 2.1.1',
_DESCRIPTION = 'tweening for lua',
_URL = 'https://github.com/kikito/tween.lua',
_LICENSE = [[
MIT LICENSE
Copyright (c) 2014 Enrique García Cota, Yuichi Tateno, Emmanuel Oga
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]
}
local pow, sin, cos, pi, sqrt, abs, asin = math.pow, math.sin, math.cos, math.pi, math.sqrt, math.abs, math.asin
-- linear
local function linear(t, b, c, d)
return c * t / d + b
end
-- quad
local function inQuad(t, b, c, d)
return c * pow(t / d, 2) + b
end
local function outQuad(t, b, c, d)
t = t / d
return -c * t * (t - 2) + b
end
local function inOutQuad(t, b, c, d)
t = t / d * 2
if t < 1 then
return c / 2 * pow(t, 2) + b
end
return -c / 2 * ((t - 1) * (t - 3) - 1) + b
end
local function outInQuad(t, b, c, d)
if t < d / 2 then
return outQuad(t * 2, b, c / 2, d)
end
return inQuad((t * 2) - d, b + c / 2, c / 2, d)
end
-- cubic
local function inCubic (t, b, c, d)
return c * pow(t / d, 3) + b
end
local function outCubic(t, b, c, d)
return c * (pow(t / d - 1, 3) + 1) + b
end
local function inOutCubic(t, b, c, d)
t = t / d * 2
if t < 1 then
return c / 2 * t * t * t + b
end
t = t - 2
return c / 2 * (t * t * t + 2) + b
end
local function outInCubic(t, b, c, d)
if t < d / 2 then
return outCubic(t * 2, b, c / 2, d)
end
return inCubic((t * 2) - d, b + c / 2, c / 2, d)
end
-- quart
local function inQuart(t, b, c, d)
return c * pow(t / d, 4) + b
end
local function outQuart(t, b, c, d)
return -c * (pow(t / d - 1, 4) - 1) + b
end
local function inOutQuart(t, b, c, d)
t = t / d * 2
if t < 1 then
return c / 2 * pow(t, 4) + b
end
return -c / 2 * (pow(t - 2, 4) - 2) + b
end
local function outInQuart(t, b, c, d)
if t < d / 2 then
return outQuart(t * 2, b, c / 2, d)
end
return inQuart((t * 2) - d, b + c / 2, c / 2, d)
end
-- quint
local function inQuint(t, b, c, d)
return c * pow(t / d, 5) + b
end
local function outQuint(t, b, c, d)
return c * (pow(t / d - 1, 5) + 1) + b
end
local function inOutQuint(t, b, c, d)
t = t / d * 2
if t < 1 then
return c / 2 * pow(t, 5) + b
end
return c / 2 * (pow(t - 2, 5) + 2) + b
end
local function outInQuint(t, b, c, d)
if t < d / 2 then
return outQuint(t * 2, b, c / 2, d)
end
return inQuint((t * 2) - d, b + c / 2, c / 2, d)
end
-- sine
local function inSine(t, b, c, d)
return -c * cos(t / d * (pi / 2)) + c + b
end
local function outSine(t, b, c, d)
return c * sin(t / d * (pi / 2)) + b
end
local function inOutSine(t, b, c, d)
return -c / 2 * (cos(pi * t / d) - 1) + b
end
local function outInSine(t, b, c, d)
if t < d / 2 then
return outSine(t * 2, b, c / 2, d)
end
return inSine((t * 2) -d, b + c / 2, c / 2, d)
end
-- expo
local function inExpo(t, b, c, d)
if t == 0 then
return b
end
return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001
end
local function outExpo(t, b, c, d)
if t == d then
return b + c
end
return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b
end
local function inOutExpo(t, b, c, d)
if t == 0 then
return b end
if t == d then
return b + c
end
t = t / d * 2
if t < 1 then
return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005
end
return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b
end
local function outInExpo(t, b, c, d)
if t < d / 2 then
return outExpo(t * 2, b, c / 2, d)
end
return inExpo((t * 2) - d, b + c / 2, c / 2, d)
end
-- circ
local function inCirc(t, b, c, d)
return(-c * (sqrt(1 - pow(t / d, 2)) - 1) + b)
end
local function outCirc(t, b, c, d)
return(c * sqrt(1 - pow(t / d - 1, 2)) + b)
end
local function inOutCirc(t, b, c, d)
t = t / d * 2
if t < 1 then
return -c / 2 * (sqrt(1 - t * t) - 1) + b
end
t = t - 2
return c / 2 * (sqrt(1 - t * t) + 1) + b
end
local function outInCirc(t, b, c, d)
if t < d / 2 then
return outCirc(t * 2, b, c / 2, d)
end
return inCirc((t * 2) - d, b + c / 2, c / 2, d)
end
-- elastic
local function calculatePAS(p,a,c,d)
p, a = p or d * 0.3, a or 0
if a < abs(c) then
return p, c, p / 4
end -- p, a, s
return p, a, p / (2 * pi) * asin(c/a) -- p,a,s
end
local function inElastic(t, b, c, d, a, p)
local s
if t == 0 then
return b
end
t = t / d
if t == 1 then
return b + c
end
p,a,s = calculatePAS(p,a,c,d)
t = t - 1
return -(a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b
end
local function outElastic(t, b, c, d, a, p)
local s
if t == 0 then
return b
end
t = t / d
if t == 1 then
return b + c
end
p,a,s = calculatePAS(p,a,c,d)
return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) + c + b
end
local function inOutElastic(t, b, c, d, a, p)
local s
if t == 0 then
return b
end
t = t / d * 2
if t == 2 then return b + c end
p,a,s = calculatePAS(p,a,c,d)
t = t - 1
if t < 0 then
return -0.5 * (a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b
end
return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p ) * 0.5 + c + b
end
local function outInElastic(t, b, c, d, a, p)
if t < d / 2 then
return outElastic(t * 2, b, c / 2, d, a, p)
end
return inElastic((t * 2) - d, b + c / 2, c / 2, d, a, p)
end
-- back
local function inBack(t, b, c, d, s)
s = s or 1.70158
t = t / d
return c * t * t * ((s + 1) * t - s) + b
end
local function outBack(t, b, c, d, s)
s = s or 1.70158
t = t / d - 1
return c * (t * t * ((s + 1) * t + s) + 1) + b
end
local function inOutBack(t, b, c, d, s)
s = (s or 1.70158) * 1.525
t = t / d * 2
if t < 1 then
return c / 2 * (t * t * ((s + 1) * t - s)) + b
end
t = t - 2
return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b
end
local function outInBack(t, b, c, d, s)
if t < d / 2 then
return outBack(t * 2, b, c / 2, d, s)
end
return inBack((t * 2) - d, b + c / 2, c / 2, d, s)
end
-- bounce
local function outBounce(t, b, c, d)
t = t / d
if t < 1 / 2.75 then
return c * (7.5625 * t * t) + b
end
if t < 2 / 2.75 then
t = t - (1.5 / 2.75)
return c * (7.5625 * t * t + 0.75) + b
elseif t < 2.5 / 2.75 then
t = t - (2.25 / 2.75)
return c * (7.5625 * t * t + 0.9375) + b
end
t = t - (2.625 / 2.75)
return c * (7.5625 * t * t + 0.984375) + b
end
local function inBounce(t, b, c, d)
return c - outBounce(d - t, 0, c, d) + b
end
local function inOutBounce(t, b, c, d)
if t < d / 2 then
return inBounce(t * 2, 0, c, d) * 0.5 + b
end
return outBounce(t * 2 - d, 0, c, d) * 0.5 + c * .5 + b
end
local function outInBounce(t, b, c, d)
if t < d / 2 then
return outBounce(t * 2, b, c / 2, d)
end
return inBounce((t * 2) - d, b + c / 2, c / 2, d)
end
tween.easing =
{
linear = linear,
inQuad = inQuad, outQuad = outQuad, inOutQuad = inOutQuad, outInQuad = outInQuad,
inCubic = inCubic, outCubic = outCubic, inOutCubic = inOutCubic, outInCubic = outInCubic,
inQuart = inQuart, outQuart = outQuart, inOutQuart = inOutQuart, outInQuart = outInQuart,
inQuint = inQuint, outQuint = outQuint, inOutQuint = inOutQuint, outInQuint = outInQuint,
inSine = inSine, outSine = outSine, inOutSine = inOutSine, outInSine = outInSine,
inExpo = inExpo, outExpo = outExpo, inOutExpo = inOutExpo, outInExpo = outInExpo,
inCirc = inCirc, outCirc = outCirc, inOutCirc = inOutCirc, outInCirc = outInCirc,
inElastic = inElastic, outElastic = outElastic, inOutElastic = inOutElastic, outInElastic = outInElastic,
inBack = inBack, outBack = outBack, inOutBack = inOutBack, outInBack = outInBack,
inBounce = inBounce, outBounce = outBounce, inOutBounce = inOutBounce, outInBounce = outInBounce
}
-- Private interface
local function copyTables(destination, keysTable, valuesTable)
valuesTable = valuesTable or keysTable
local mt = getmetatable(keysTable)
if mt and getmetatable(destination) == nil then
setmetatable(destination, mt)
end
for k,v in pairs(keysTable) do
if type(v) == 'table' then
destination[k] = copyTables({}, v, valuesTable[k])
else
destination[k] = valuesTable[k]
end
end
return destination
end
local function checkSubjectAndTargetRecursively(subject, target, path)
path = path or {}
local targetType, newPath
for k,targetValue in pairs(target) do
targetType, newPath = type(targetValue), copyTables({}, path)
table.insert(newPath, tostring(k))
if targetType == 'number' then
assert(type(subject[k]) == 'number', "Parameter '" .. table.concat(newPath,'/') .. "' is missing from subject or isn't a number")
elseif targetType == 'table' then
checkSubjectAndTargetRecursively(subject[k], targetValue, newPath)
else
assert(targetType == 'number', "Parameter '" .. table.concat(newPath,'/') .. "' must be a number or table of numbers")
end
end
end
local function checkNewParams(initial, duration, subject, target, easing)
-- assert(type(initial) == 'number' and duration > 0, "duration must be a positive number. Was " .. tostring(duration))
-- assert(type(duration) == 'number' and duration > 0, "duration must be a positive number. Was " .. tostring(duration))
assert(type(easing)=='function', "easing must be a function. Was " .. tostring(easing))
if subject and target then
local tsubject = type(subject)
assert(tsubject == 'table' or tsubject == 'userdata', "subject must be a table or userdata. Was " .. tostring(subject))
assert(type(target)== 'table', "target must be a table. Was " .. tostring(target))
checkSubjectAndTargetRecursively(subject, target)
end
end
local function getEasingFunction(easing)
easing = easing or "linear"
if type(easing) == 'string' then
local name = easing
easing = tween.easing[name]
if type(easing) ~= 'function' then
error("The easing function name '" .. name .. "' is invalid")
end
end
return easing
end
local function performEasingOnSubject(subject, target, initial, clock, duration, easing)
local t,b,c,d
for k,v in pairs(target) do
if type(v) == 'table' then
performEasingOnSubject(subject[k], v, initial[k], clock, duration, easing)
else
t,b,c,d = clock, initial[k], v - initial[k], duration
subject[k] = easing(t,b,c,d)
end
end
end
local function performEasing(table, initial, target, clock, duration, easing)
if type(target) == "table" then
local t,b,c,d
for k, v in pairs(target) do
if type(v) == 'table' then
table[k] = {}
performEasing(table[k], initial[k], v, clock, duration, easing)
else
t,b,c,d = clock, initial[k], v - initial[k], duration
table[k] = easing(t,b,c,d)
end
end
return table
else
local t, b, c, d = clock, initial, target - initial, duration
return easing(t,b,c,d)
end
end
-- Public interface
local Tween = {}
function Tween:set(clock)
assert(type(clock) == 'number', "clock must be a positive number or 0")
if self.subject and self.initial == 0 then
self.initial = copyTables({}, self.target, self.subject)
end
self.clock = clock
if self.clock <= 0 then
self.clock = 0
if self.subject then
copyTables(self.subject, self.initial)
end
elseif self.clock >= self.duration then -- the tween has expired
self.clock = self.duration
if self.subject then
copyTables(self.subject, self.target)
end
else
if self.subject then
performEasingOnSubject(self.subject, self.target, self.initial, self.clock, self.duration, self.easing)
else
local pos = {}
return performEasing(pos, self.initial, self.target, self.clock, self.duration, self.easing)
end
end
return self.clock >= self.duration
end
function Tween:update(dt)
assert(type(dt) == 'number', "dt must be a number")
return self:set(self.clock + dt)
end
function Tween:reset()
return self:set(0)
end
function tween.new(args)
args = args or {}
args.initial = args.initial or 0
args.subject = args.subject or nil
args.target = args.target or nil
args.duration = args.duration or 0
args.easing = args.easing or nil
args.easing = getEasingFunction(args.easing)
checkNewParams(args.initial, args.duration, args.subject, args.target, args.easing)
local ret = gobject{}
ret.clock = 0
gtable.crush(ret, args, true)
gtable.crush(ret, Tween, true)
return ret
end
return tween

View File

@ -1,21 +1,97 @@
local wibox = require("wibox")
local beautiful = require("beautiful")
local dpi = beautiful.xresources.apply_dpi
local awful = require("awful")
local gears = require("gears")
local rubato = require(tostring(...):match(".*awedock") .. ".rubato")
local chel = require("module.dock_helpers").color
local rubato = require("module.rubato")
local function helpers()
local function overloaded()
local fns = {}
local mt = {}
local function oerror()
return error("Invalid argument types to overloaded function")
end
function mt:__call(...)
local arg = { ... }
local default = self.default
local signature = {}
for i, arg in ipairs({ ... }) do
signature[i] = type(arg)
end
signature = table.concat(signature, ",")
return (fns[signature] or self.default)(...)
end
function mt:__index(key)
local signature = {}
local function __newindex(self, key, value)
print(key, type(key), value, type(value))
signature[#signature + 1] = key
fns[table.concat(signature, ",")] = value
print("bind", table.concat(signature, ", "))
end
local function __index(self, key)
print("I", key, type(key))
signature[#signature + 1] = key
return setmetatable({}, { __index = __index, __newindex = __newindex })
end
return __index(self, key)
end
function mt:__newindex(key, value)
fns[key] = value
end
return setmetatable({ default = oerror }, mt)
end
--}}}
local dpi = beautiful.xresources.apply_dpi
local function dec_hex(IN)
local B, K, OUT, I, D = 16, "0123456789ABCDEF", "", 0
while IN > 0 do
I = I + 1
IN, D = math.floor(IN / B), (IN % B) + 1
OUT = string.sub(K, D, D) .. OUT
end
return #OUT == 2 and OUT or "0" .. OUT
end
local function init(s, h, o, shape, pinneds)
--local function init(args)
--[[ if args.screen == nil then return end
-- local s = args.screen
-- local h = args.height or dpi(50)
-- local o = args.offset or 0
-- local shape = args.shape or gears.shape.rectangle
-- local pinneds = args.pinneds or nil]]
-- color helpers {{{
local color = {}
color.col_shift = overloaded()
color.col_shift.string.number = function(c, s)
local r, g, b, o = gears.color.parse_color(c)
return "#" .. dec_hex(r * 255 + s) .. dec_hex(g * 255 + s) .. dec_hex(b * 255 + s) .. dec_hex(o * 255)
end
color.col_shift.string.number.number.number = function(c, sr, sg, sb)
local r, g, b, o = gears.color.parse_color(c)
return "#" .. dec_hex(r * 255 + sr) .. dec_hex(g * 255 + sg) .. dec_hex(b * 255 + sb) .. dec_hex(o * 255)
end
color.col_shift.string.number.number.number.number = function(c, sr, sg, sb, so)
local r, g, b, o = gears.color.parse_color(c)
return "#" .. dec_hex(r * 255 + sr) .. dec_hex(g * 255 + sg) .. dec_hex(b * 255 + sb) .. dec_hex(o * 255 + so)
end
color.col_diff = function(f, s)
local fr, fg, fb, fo = gears.color.parse_color(f)
local sr, sg, sb, so = gears.color.parse_color(s)
return sr - fr, sg - fg, sb - fb, so - fo
end
--}}}
return {
color = color,
}
end
local chel = helpers().color
--local function init(s, h, o, shape, pinneds)
local function init(args)
local s = args.screen
local h = args.height or dpi(50)
local o = args.offset or 5
local inner_shape = args.inner_shape or gears.shape.rectangle
local outer_shape = args.outer_shape or gears.shape.rectangle
local pinneds = args.pinneds
-- tasklist creation {{{
local tasklist = awful.widget.tasklist({
@ -29,9 +105,6 @@ local function init(s, h, o, shape, pinneds)
end, --sorts clients in order of their tags
filter = awful.widget.tasklist.filter.alltags,
forced_height = h,
style = {
shape = shape,
},
layout = {
layout = wibox.layout.fixed.horizontal,
},
@ -55,8 +128,8 @@ local function init(s, h, o, shape, pinneds)
forced_height = h / 10,
forced_width = h / 10,
id = "status",
bg = beautiful.dock_focused_bg,
shape = shape,
bg = beautiful.dock_unfocused_indicator_bg or "#484E5B",
shape = inner_shape,
widget = wibox.container.background,
},
widget = wibox.container.place, --so the bg widget doesnt get stretched
@ -65,8 +138,8 @@ local function init(s, h, o, shape, pinneds)
},
id = "bg",
widget = wibox.container.background,
bg = beautiful.dock_bg,
shape = shape,
bg = beautiful.dock_bg or "#061115",
shape = inner_shape,
},
widget = wibox.container.margin,
margins = h / 10,
@ -81,7 +154,7 @@ local function init(s, h, o, shape, pinneds)
rate = 30,
pos = p,
subscribed = function(pos)
self:get_children_by_id("bg")[1].bg = chel.col_shift(beautiful.dock_bg, pos)
self:get_children_by_id("bg")[1].bg = chel.col_shift(beautiful.dock_bg or "#061115", pos)
end,
})
on_hover.target = t
@ -119,8 +192,8 @@ local function init(s, h, o, shape, pinneds)
self:get_children_by_id("status")[1].forced_width = pos
end,
})
local bg_col = beautiful.dock_focused_bg
local bg_focus_col = beautiful.dock_accent
local bg_col = beautiful.dock_unfocused_indicator_bg or "#484E5B"
local bg_focus_col = beautiful.dock_indicator_bg or "#6791C9"
local sh_r, sh_g, sh_b, _ = chel.col_diff(bg_col, bg_focus_col)
local status_c = rubato.timed({
@ -182,7 +255,7 @@ local function init(s, h, o, shape, pinneds)
forced_height = h / 10,
forced_width = h / 10,
id = "status",
shape = shape,
shape = inner_shape,
widget = wibox.container.background,
},
widget = wibox.container.place, --so the bg widget doesnt get stretched
@ -191,7 +264,7 @@ local function init(s, h, o, shape, pinneds)
layout = wibox.layout.align.vertical,
},
widget = wibox.container.background,
shape = shape,
shape = inner_shape,
id = "bg",
buttons = awful.button({}, 1, function()
awful.spawn.easy_async(p.start_cmd)
@ -211,7 +284,7 @@ local function init(s, h, o, shape, pinneds)
rate = 30,
pos = po,
subscribed = function(pos)
self:get_children_by_id("bg")[1].bg = chel.col_shift(beautiful.dock_bg, pos)
self:get_children_by_id("bg")[1].bg = chel.col_shift(beautiful.dock_bg or "#061115", pos)
end,
})
on_hover.target = t
@ -237,24 +310,20 @@ local function init(s, h, o, shape, pinneds)
screen = s,
x = s.geometry.x + s.geometry.width / 2,
y = s.geometry.y + s.geometry.height - (h + o),
shape = shape,
shape = outer_shape,
widget = {
{
{
{
pinned_apps,
tasklist,
layout = wibox.layout.fixed.horizontal,
},
widget = wibox.container.margin,
margin = dpi(5),
pinned_apps,
tasklist,
layout = wibox.layout.fixed.horizontal,
},
widget = wibox.container.background,
bg = beautiful.dock_bg,
shape = shape,
widget = wibox.container.margin,
margin = dpi(5),
},
widget = wibox.container.place,
halign = "center",
widget = wibox.container.background,
bg = beautiful.dock_bg or "#061115",
shape = inner_shape,
},
})
@ -275,7 +344,7 @@ local function init(s, h, o, shape, pinneds)
end,
})
local autohidetimer = gears.timer({
timeout = 1,
timeout = 2,
single_shot = true,
callback = function()
autohideanim.target = 0

View File

@ -0,0 +1 @@
tags

View File

Before

Width:  |  Height:  |  Size: 809 KiB

After

Width:  |  Height:  |  Size: 809 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 248 KiB

After

Width:  |  Height:  |  Size: 248 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -195,10 +195,10 @@ local function timed(args)
timer:stop() --stops itself
--run subscribed in functions
obj:fire(obj.pos, time, dx)
obj:fire(obj.pos, obj.duration, dx)
-- awestore compatibility...
if obj.awestore_compat then obj.ended:fire(obj.pos, time, dx) end
if obj.awestore_compat then obj.ended:fire(obj.pos, obj.duration, dx) end
--otherwise it just fires normally
else obj:fire(obj.pos, time, dx) end

View File

@ -0,0 +1 @@
doc/

View File

@ -0,0 +1,84 @@
# UPower Battery Widget
This is my re-implementation of the [awesome-upower-battery][awesome-upower-battery-repository] by [berlam][berlam]. This widget has a lot of potencial for the [Awesome WM][AwesomeWM] and I wanted to hack it a bit for my personal use.
UPower is an abstraction for power devices. You can use it to access advanced statistics about your power devices.
UPowerGlib is a Glib interface to access data exposed by UPower.
The Awesome WM uses LGI as an interpolation layer for Glib with Lua. So, you can access the UPowerGlib interface directly from your lua code.
Thanks to this, we can write battery widget relaying on realtime data pushed from the UPower daemon itself. So the battery widget as no charge on the system (no need to pull data every X seconds) and provides more accurate data to the user.
## Usage
When creating an instance of this widget, you can specify the `widget_template` you want to use and how the widget updates. It gives you the control on how the widget should display the battery status.
You can generate the API documentation with [ldoc][ldoc].
```sh
ldoc -c config.ld init.lua
```
Here is an example of implementation using a [`wibox.widget.textbox`][awesome-api-wibox.widget.textbox] widget to display the battery percentage:
```lua
-- Load the module:
local battery_widget = require 'battery_widget'
-- Create the battery widget:
local my_battery_widget = battery_widget {
screen = screen,
use_display_device = true,
widget_template = wibox.widget.textbox
}
-- When UPower updates the battery status, the widget is notified
-- and calls a signal you need to connect to:
my_battery_widget:connect_signal('upower::update', function (widget, device)
widget.text = string.format('%3d', device.percentage) .. '%'
end)
```
### Using different devices
With the parameter `use_display_device = true`, the battery widget will automatically monitor the _display device_.
If you want to manually set which device to monitor, you can use the `device_path` parameter.
```lua
local my_battery_widget = battery_widget{
screen = s,
device_path = '/org/freedesktop/UPower/devices/battery_BAT0',
widget_template = wibox.widget.textbox
}
```
You can check the API documentation to read more about statics function to help you to identify your devices.
### Battery widget not appearing
When creating a new instance of `battery_widget`, the widget will not be shown. The widget waits an update from UPower to call the "upower::update" signal and use your attached callback to update (and draw) the widget.
You can however use one of the following method to force the widget to be drawn at its creation:
* Use the parameter `instant_update` to explicitly ask the battery_widget to call the "upower::update" signal at the next Awesome WM cycle.
* Use the parameter `create_callback` to use your own code to initialize the widget. (This callback await the same arguments than the "upower::update" signal)
You can read more about these parameters in the API documentation.
## Dependencies
* [Awesome WM][AwesomeWM]
* [UPower][UPower]
* [UPowerGlib][UPowerGlib]
## Acknowledgment
Thanks a lot to [berlam][berlam] for the initial code and the idea to use the UPowerGlib interface 🚀.
[awesome-upower-battery-repository]: https://github.com/berlam/awesome-upower-battery
[berlam]: https://github.com/berlam
[AwesomeWM]: https://awesomewm.org/
[awesome-api-wibox.widget.textbox]: https://awesomewm.org/apidoc/widgets/wibox.widget.textbox.html
[UPower]: https://upower.freedesktop.org/
[UPowerGlib]: https://lazka.github.io/pgi-docs/UPowerGlib-1.0/index.html
[ldoc]: https://stevedonovan.github.io/ldoc/

View File

@ -0,0 +1,24 @@
-- Configuration file for ldoc
project = 'battery_widget'
title = 'Awesome WM - Battery Widget'
all = false
dir = 'doc'
format='markdown'
pretty = 'lua'
prettify_files = true
backtick_references = true
merge = true
use_markdown_titles = true
wrap = true
sort_modules = true
not_luadoc = true
-- Define some new ldoc tags from the AwesomeWM doc
new_type("staticfct", "Static functions", false, "Parameters")
new_type("constructorfct", "Constructor", false, "Parameters")
new_type("method", "Object methods", false, "Parameters")
new_type("property", "Object properties", false, "Type")
new_type("signal", "Signals", false, "Arguments")

View File

@ -0,0 +1,140 @@
---------------------------------------------------------------------------
-- A battery widget based on the UPower deamon.
--
-- @author Aire-One
-- @copyright 2020 Aire-One
---------------------------------------------------------------------------
local upower = require('lgi').require('UPowerGlib')
local gtable = require 'gears.table'
local gtimer = require 'gears.timer'
local wbase = require 'wibox.widget.base'
local setmetatable = setmetatable -- luacheck: ignore setmetatable
local battery_widget = {}
local mt = {}
--- Helper to get the path of all connected power devices.
-- @treturn table The list of all power devices path.
-- @staticfct battery_widget.list_devices
function battery_widget.list_devices()
local ret = {}
local devices = upower.Client():get_devices()
for _,d in ipairs(devices) do
table.insert(ret, d:get_object_path())
end
return ret
end
--- Helper function to get a device instance from its path.
-- @tparam string path The path of the device to get.
-- @treturn UPowerGlib.Device|nil The device if it was found, `nil` otherwise.
-- @staticfct battery_widget.get_device
function battery_widget.get_device(path)
local devices = upower.Client():get_devices()
for _,d in ipairs(devices) do
if d:get_object_path() == path then
return d
end
end
return nil
end
--- Helper function to easily get the default BAT0 device path without.
-- @treturn string The BAT0 device path.
-- @staticfct battery_widget.get_BAT0_device_path
function battery_widget.get_BAT0_device_path()
local bat0_path = '/org/freedesktop/UPower/devices/battery_BAT0'
return bat0_path
end
--- Helper function to convert seconds into a human readable clock string.
--
-- This translates the given seconds parameter into a human readable string
-- following the notation `HH:MM` (where HH is the number of hours and MM the
-- number of minutes).
-- @tparam number seconds The umber of seconds to translate.
-- @treturn string The human readable generated clock string.
-- @staticfct battery_widget.to_clock
function battery_widget.to_clock(seconds)
if seconds <= 0 then
return '00:00';
else
local hours = string.format('%02.f', math.floor(seconds/3600));
local mins = string.format('%02.f', math.floor(seconds/60 - hours*60));
return hours .. ':' .. mins
end
end
--- Gives the default widget to use if user didn't specify one.
-- The default widget used is an `empty_widget` instance.
-- @treturn widget The default widget to use.
local function default_template ()
return wbase.empty_widget()
end
--- The device monitored by the widget.
-- @property device
-- @tparam UPowerGlib.Device device
--- Emited when the UPower device notify an update.
-- @signal upower::update
-- @tparam battery_widget widget The widget.
-- @tparam UPowerGlib.Device device The Upower device.
--- battery_widget constructor.
--
-- This function creates a new `battery_widget` instance. This widget watches
-- the `display_device` status and report.
-- @tparam table args The arguments table.
-- @tparam[opt] widget args.widget_template The widget template to use to
-- create the widget instance.
-- @tparam[opt] string args.device_path Path of the device to monitor.
-- @tparam[opt=false] boolean args.use_display_device Should the widget monitor
-- the _display device_?
-- @tparam[opt] boolean args.instant_update Call an update cycle right after the
-- widget creation.
-- @treturn battery_widget The battery_widget instance build.
-- @constructorfct battery_widget.new
function battery_widget.new (args)
args = gtable.crush({
widget_template = default_template(),
device_path = '',
use_display_device = false
}, args or {})
local widget = wbase.make_widget_from_value(args.widget_template)
widget.device = args.use_display_device
and upower.Client():get_display_device()
or battery_widget.get_device(args.device_path)
-- Attach signals:
widget.device.on_notify = function (d)
widget:emit_signal('upower::update', d)
end
-- Call an update cycle if the user asked to instan update the widget.
if args.instant_update then
gtimer.delayed_call(widget.emit_signal, widget, 'upower::update', widget.device)
end
return widget
end
function mt.__call(self, ...)
return battery_widget.new(...)
end
return setmetatable(battery_widget, mt)

View File

View File

@ -0,0 +1,23 @@
- [Home](home.md)
- [Layouts](layouts/layout.md)
- Modules
- [Flash Focus](module/flash.md)
- [Tabbed](module/tabbed.md)
- [Tiled Wallpaper](module/twall.md)
- [Wallpaper Easy Setup](module/wall.md)
- [Window Swallowing](module/swal.md)
- [Scratchpad](module/scratch.md)
- Signals
- [Playerctl](signals/pctl.md)
- Widgets
- [Tag Preview](widgets/tag_preview.md)
- [Task Preview](widgets/task_preview.md)
- [Tabbed Misc](widgets/tabbed_misc.md)
- [Window Switcher](widgets/window_switcher.md)
- Extra
- [Theme Variable Template](theme.md)

View File

@ -0,0 +1,34 @@
# <center> 🌟 Bling - Utilities for AwesomeWM 🌟 </center>
## Why
[AwesomeWM](https://awesomewm.org/) is literally what it stands for, an awesome window manager.
Its unique selling point has always been the widget system, which allows for fancy buttons, sliders, bars, dashboards and anything you can imagine. But that feature can be a curse. Most modules focus on the widget side of things which leave the actual window managing part of AwesomeWM underdeveloped compared to, for example, [xmonad](https://xmonad.org/) even though it's probably just as powerfull in that area.
This project focuses on that problem - adding new layouts and modules that make use of the widget system, but primarily focus on the new window managing features.
## Installation
- clone this repo into your `~/.config/awesome` folder
- `git clone https://github.com/BlingCorp/bling.git ~/.config/awesome/bling`
- require the module in your `rc.lua`, and make sure it's under the beautiful module initialization
```lua
-- other imports
local beautiful = require("beautiful")
-- other configuration stuff here
beautiful.init("some_theme.lua")
local bling = require("bling")
```
## Contributors
A special thanks to all our contributors...
<a href="https://github.com/BlingCorp/bling/graphs/contributors">
<img src="https://contrib.rocks/image?repo=BlingCorp/bling" />
</a>
Made with [contributors-img](https://contrib.rocks).

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Bling Docs</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Utilities for the awesome window manager">
<meta name="og:image" content="https://raw.githubusercontent.com/BlingCorp/bling/master/images/bling_banner.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="javacafe.css">
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: 'Bling',
nameLink: '/',
repo: 'https://github.com/BlingCorp/bling',
loadSidebar: true,
subMaxLevel: 3,
homepage: 'home.md'
}
</script>
<!-- Docsify v4 -->
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<script src="//unpkg.com/prismjs/components/prism-lua.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,65 @@
## 📎 Layouts <!-- {docsify-ignore} -->
Choose layouts from the list below and add them to to your `awful.layouts` list in your `rc.lua`.
Everyone of them supports multiple master clients and master width factor making them easy to use.
The mstab layout uses the tab theme from the tabbed module.
```lua
bling.layout.mstab
bling.layout.centered
bling.layout.vertical
bling.layout.horizontal
bling.layout.equalarea
bling.layout.deck
```
### Theme Variables
```lua
-- mstab
theme.mstab_bar_disable = false -- disable the tabbar
theme.mstab_bar_ontop = false -- whether you want to allow the bar to be ontop of clients
theme.mstab_dont_resize_slaves = false -- whether the tabbed stack windows should be smaller than the
-- currently focused stack window (set it to true if you use
-- transparent terminals. False if you use shadows on solid ones
theme.mstab_bar_padding = "default" -- how much padding there should be between clients and your tabbar
-- by default it will adjust based on your useless gaps.
-- If you want a custom value. Set it to the number of pixels (int)
theme.mstab_border_radius = 0 -- border radius of the tabbar
theme.mstab_bar_height = 40 -- height of the tabbar
theme.mstab_tabbar_position = "top" -- position of the tabbar (mstab currently does not support left,right)
theme.mstab_tabbar_style = "default" -- style of the tabbar ("default", "boxes" or "modern")
-- defaults to the tabbar_style so only change if you want a
-- different style for mstab and tabbed
```
### Previews
#### Mstab (dynamic tabbing layout)
![](https://imgur.com/HZRgApE.png)
*screenshot by [JavaCafe01](https://github.com/JavaCafe01)*
#### Centered
![](https://media.discordapp.net/attachments/769673106842845194/780095998239834142/unknown.png)
*screenshot by [HeavyRain266](https://github.com/HeavyRain266)*
#### Equal area
![](https://imgur.com/JCFFywv.png)
*screenshot by [bysmutheye](https://github.com/bysmutheye)*
#### Deck
The left area shows the deck layout in action. In this screenshot it is used together with [layout machi](https://github.com/xinhaoyuan/layout-machi) and its sublayout support.
![](https://cdn.discordapp.com/attachments/635625954219261982/877957824225894430/unknown.png)
*screenshot by [JavaCafe01](https://github.com/JavaCafe01)*

View File

@ -0,0 +1,33 @@
## 🔦 Flash Focus <!-- {docsify-ignore} -->
Flash focus does an opacity animation effect on a client when it is focused.
### Usage
There are two ways in which you can use this module. You can enable it by calling the `enable()` function:
```lua
bling.module.flash_focus.enable()
```
This connects to the focus signal of a client, which means that the flash focus will activate however you focus the client.
The other way is to call the function itself like this: `bling.module.flash_focus.flashfocus(someclient)`. This allows you to activate on certain keybinds like so:
```lua
awful.key({modkey}, "Up",
function()
awful.client.focus.bydirection("up")
bling.module.flash_focus.flashfocus(client.focus)
end, {description = "focus up", group = "client"})
```
### Theme Variables
```lua
theme.flash_focus_start_opacity = 0.6 -- the starting opacity
theme.flash_focus_step = 0.01 -- the step of animation
```
### Preview
![](https://imgur.com/5txYrlV.gif)
*gif by [JavaCafe01](https://github.com/JavaCafe01)*

View File

@ -0,0 +1,75 @@
## 🍃 Scratchpad <!-- {docsify-ignore} -->
An easy way to create multiple scratchpads.
### A... what?
You can think about a scratchpad as a window whose visibility can be toggled, but still runs in the background without being visible (or minimized) most of the time. Many people use it to have one terminal in which to perform minor tasks, but it is the most useful for windows which only need a couple seconds in between your actual activity, such as music players or chat applications.
### Rubato Animation Support
#### Awestore is now deprecated from Bling, we are switching to Rubato.
Please go over to the [rubato](https://github.com/andOrlando/rubato) repository for installation instructions. Give it a star as well! The animations are completely optional, and if you choose not to use it, you do not need rubato installed.
### Usage
To initalize a scratchpad you can do something like the following:
```lua
local bling = require("bling")
local rubato = require("rubato") -- Totally optional, only required if you are using animations.
-- These are example rubato tables. You can use one for just y, just x, or both.
-- The duration and easing is up to you. Please check out the rubato docs to learn more.
local anim_y = rubato.timed {
pos = 1090,
rate = 60,
easing = rubato.quadratic,
intro = 0.1,
duration = 0.3,
awestore_compat = true -- This option must be set to true.
}
local anim_x = rubato.timed {
pos = -970,
rate = 60,
easing = rubato.quadratic,
intro = 0.1,
duration = 0.3,
awestore_compat = true -- This option must be set to true.
}
local term_scratch = bling.module.scratchpad {
command = "wezterm start --class spad", -- How to spawn the scratchpad
rule = { instance = "spad" }, -- The rule that the scratchpad will be searched by
sticky = true, -- Whether the scratchpad should be sticky
autoclose = true, -- Whether it should hide itself when losing focus
floating = true, -- Whether it should be floating (MUST BE TRUE FOR ANIMATIONS)
geometry = {x=360, y=90, height=900, width=1200}, -- The geometry in a floating state
reapply = true, -- Whether all those properties should be reapplied on every new opening of the scratchpad (MUST BE TRUE FOR ANIMATIONS)
dont_focus_before_close = false, -- When set to true, the scratchpad will be closed by the toggle function regardless of whether its focused or not. When set to false, the toggle function will first bring the scratchpad into focus and only close it on a second call
rubato = {x = anim_x, y = anim_y} -- Optional. This is how you can pass in the rubato tables for animations. If you don't want animations, you can ignore this option.
}
```
Once initalized, you can use the object (which in this case is named `term_scratch`) like this:
```lua
term_scratch:toggle() -- toggles the scratchpads visibility
term_scratch:turn_on() -- turns the scratchpads visibility on
term_scratch:turn_off() -- turns the scratchpads visibility off
```
You can also connect to signals as you are used to for further customization. For example like that:
```lua
term_scratch:connect_signal("turn_on", function(c) naughty.notify({title = "Turned on!"}) end)
```
The following signals are currently available. `turn_on`, `turn_off` and `inital_apply` pass the client on which they operated as an argument:
- `turn_on` fires when the scratchpad is turned on on a tag that it wasn't present on before
- `turn_off` fires when the scratchpad is turned off on a tag
- `spawn` fires when the scratchpad is launched with the given command
- `inital_apply` fires after `spawn`, when a corresponding client has been found and the properties have been applied

View File

@ -0,0 +1,25 @@
## 😋 Window Swallowing <!-- {docsify-ignore} -->
Can your window manager swallow? It probably can...
### Usage
To activate and deactivate window swallowing here are the following functions. If you want to activate it, just call the `start` function once in your `rc.lua`.
```lua
bling.module.window_swallowing.start() -- activates window swallowing
bling.module.window_swallowing.stop() -- deactivates window swallowing
bling.module.window_swallowing.toggle() -- toggles window swallowing
```
### Theme Variables
```lua
theme.parent_filter_list = {"firefox", "Gimp"} -- class names list of parents that should not be swallowed
theme.child_filter_list = { "Dragon" } -- class names list that should not swallow their parents
theme.swallowing_filter = true -- whether the filters above should be active
```
### Preview
![](https://media.discordapp.net/attachments/635625813143978012/769180910683684864/20-10-23-14-40-32.gif)
*gif by [Nooo37](https://github.com/Nooo37)*

View File

@ -0,0 +1,66 @@
## 📑 Tabbed <!-- {docsify-ignore} -->
Tabbed implements a tab container. There are also different themes for the tabs.
### Usage
You should bind these functions to keys in order to use the tabbed module effectively:
```lua
bling.module.tabbed.pick() -- picks a client with your cursor to add to the tabbing group
bling.module.tabbed.pop() -- removes the focused client from the tabbing group
bling.module.tabbed.iter() -- iterates through the currently focused tabbing group
bling.module.tabbed.pick_with_dmenu() -- picks a client with a dmenu application (defaults to rofi, other options can be set with a string parameter like "dmenu")
bling.module.tabbed.pick_by_direction(dir) -- picks a client based on direction ("up", "down", "left" or "right")
```
### Theme Variables
```lua
-- For tabbed only
theme.tabbed_spawn_in_tab = false -- whether a new client should spawn into the focused tabbing container
-- For tabbar in general
theme.tabbar_ontop = false
theme.tabbar_radius = 0 -- border radius of the tabbar
theme.tabbar_style = "default" -- style of the tabbar ("default", "boxes" or "modern")
theme.tabbar_font = "Sans 11" -- font of the tabbar
theme.tabbar_size = 40 -- size of the tabbar
theme.tabbar_position = "top" -- position of the tabbar
theme.tabbar_bg_normal = "#000000" -- background color of the focused client on the tabbar
theme.tabbar_fg_normal = "#ffffff" -- foreground color of the focused client on the tabbar
theme.tabbar_bg_focus = "#1A2026" -- background color of unfocused clients on the tabbar
theme.tabbar_fg_focus = "#ff0000" -- foreground color of unfocused clients on the tabbar
theme.tabbar_bg_focus_inactive = nil -- background color of the focused client on the tabbar when inactive
theme.tabbar_fg_focus_inactive = nil -- foreground color of the focused client on the tabbar when inactive
theme.tabbar_bg_normal_inactive = nil -- background color of unfocused clients on the tabbar when inactive
theme.tabbar_fg_normal_inactive = nil -- foreground color of unfocused clients on the tabbar when inactive
theme.tabbar_disable = false -- disable the tab bar entirely
-- the following variables are currently only for the "modern" tabbar style
theme.tabbar_color_close = "#f9929b" -- chnges the color of the close button
theme.tabbar_color_min = "#fbdf90" -- chnges the color of the minimize button
theme.tabbar_color_float = "#ccaced" -- chnges the color of the float button
```
### Preview
Modern theme:
<img src="https://imgur.com/omowmIQ.png" width="600"/>
*screenshot by [JavaCafe01](https://github.com/JavaCafe01)*
### Signals
The tabbed module emits a few signals for the purpose of integration,
```lua
-- bling::tabbed::update -- triggered whenever a tabbed object is updated
-- tabobj -- the object that caused the update
-- bling::tabbed::client_added -- triggered whenever a new client is added to a tab group
-- tabobj -- the object that the client was added to
-- client -- the client that added
-- bling::tabbed::client_removed -- triggered whenever a client is removed from a tab group
-- tabobj -- the object that the client was removed from
-- client -- the client that was removed
-- bling::tabbed::changed_focus -- triggered whenever a tab group's focus is changed
-- tabobj -- the modified tab group
```

View File

@ -0,0 +1,26 @@
## 🏬 Tiled Wallpaper <!-- {docsify-ignore} -->
### Usage
The function to set an automatically created tiled wallpaper can be called the following way (you don't need to set every option in the table):
```lua
awful.screen.connect_for_each_screen(function(s) -- that way the wallpaper is applied to every screen
bling.module.tiled_wallpaper("x", s, { -- call the actual function ("x" is the string that will be tiled)
fg = "#ff0000", -- define the foreground color
bg = "#00ffff", -- define the background color
offset_y = 25, -- set a y offset
offset_x = 25, -- set a x offset
font = "Hack", -- set the font (without the size)
font_size = 14, -- set the font size
padding = 100, -- set padding (default is 100)
zickzack = true -- rectangular pattern or criss cross
})
end)
```
### Preview
![](https://media.discordapp.net/attachments/702548913999314964/773887721294135296/tiled-wallpapers.png?width=1920&height=1080)
*screenshots by [Nooo37](https://github.com/Nooo37)*

View File

@ -0,0 +1,142 @@
## 🎇 Wallpaper Easy Setup <!-- {docsify-ignore} -->
This is a simple-to-use, extensible, declarative wallpaper manager.
### Practical Examples
```lua
-- A default Awesome wallpaper
bling.module.wallpaper.setup()
-- A slideshow with pictures from different sources changing every 30 minutes
bling.module.wallpaper.setup {
wallpaper = {"/images/my_dog.jpg", "/images/my_cat.jpg"},
change_timer = 1800
}
-- A random wallpaper with images from multiple folders
bling.module.wallpaper.setup {
set_function = bling.module.wallpaper.setters.random,
wallpaper = {"/path/to/a/folder", "/path/to/another/folder"},
change_timer = 631, -- prime numbers are better for timers
position = "fit",
background = "#424242"
}
-- wallpapers based on a schedule, like awesome-glorious-widgets dynamic wallpaper
-- https://github.com/manilarome/awesome-glorious-widgets/tree/master/dynamic-wallpaper
bling.module.wallpaper.setup {
set_function = bling.module.wallpaper.setters.simple_schedule,
wallpaper = {
["06:22:00"] = "morning-wallpaper.jpg",
["12:00:00"] = "noon-wallpaper.jpg",
["17:58:00"] = "night-wallpaper.jpg",
["24:00:00"] = "midnight-wallpaper.jpg",
},
position = "maximized",
}
-- random wallpapers, from different folder depending on time of the day
bling.module.wallpaper.setup {
set_function = bling.module.wallpaper.setters.simple_schedule,
wallpaper = {
["09:00:00"] = "~/Pictures/safe_for_work",
["18:00:00"] = "~/Pictures/personal",
},
schedule_set_function = bling.module.wallpaper.setters.random
position = "maximized",
recursive = false,
change_timer = 600
}
-- setup for multiple screens at once
-- the 'screen' argument can be a table of screen objects
bling.module.wallpaper.setup {
set_function = bling.module.wallpaper.setters.random,
screen = screen, -- The awesome 'screen' variable is an array of all screen objects
wallpaper = {"/path/to/a/folder", "/path/to/another/folder"},
change_timer = 631
}
```
### Details
The setup function will do 2 things: call the set-function when awesome requests a wallpaper, and manage a timer to call `set_function` periodically.
Its argument is a args table that is passed to ohter functions (setters and wallpaper functions), so you define everything with setup.
The `set_function` is a function called every times a wallpaper is needed.
The module provides some setters:
* `bling.module.wallpaper.setters.awesome_wallpaper`: beautiful.theme_assets.wallpaper with defaults from beautiful.
* `bling.module.wallpaper.setters.simple`: slideshow from the `wallpaper` argument.
* `bling.module.wallpaper.setters.random`: same as simple but in a random way.
* `bling.module.wallpaper.setters.simple_schedule`: takes a table of `["HH:MM:SS"] = wallpaper` arguments, where wallpaper is the `wallpaper` argument used by `schedule_set_function`.
A wallpaper is one of the following elements:
* a color
* an image
* a folder containing images
* a function that sets a wallpaper
* everything gears.wallpaper functions can manage (cairo surface, cairo pattern string)
* a list containing any of the elements above
To set up for multiple screens, two possible methods are:
* Call the `setup` function for each screen, passing the appropriate configuration and `screen` arg
* Call the `setup` function once, passing a table of screens as the `screen` arg. This applies the same configuration to all screens in the table
_Note_: Multiple screen setup only works for the `simple` and `random` setters
```lua
-- This is a valid wallpaper definition
bling.module.wallpaper.setup {
wallpaper = { -- a list
"black", "#112233", -- colors
"wall1.jpg", "wall2.png", -- files
"/path/to/wallpapers", -- folders
-- cairo patterns
"radial:600,50,100:105,550,900:0,#2200ff:0.5,#00ff00:1,#101010",
-- or functions that set a wallpaper
function(args) bling.module.tiled_wallpaper("\\o/", args.screen) end,
bling.module.wallpaper.setters.awesome_wallpaper,
},
change_timer = 10,
}
```
The provided setters `simple` and `random` will use 2 internal functions that you can use to write your own setter:
* `bling.module.wallpaper.prepare_list`: return a list of wallpapers directly usable by `apply` (for now, it just explores folders)
* `bling.module.wallpaper.apply`: a wrapper for gears.wallpaper functions, using the args table of setup
Here are the defaults:
```lua
-- Default parameters
bling.module.wallpaper.setup {
screen = nil, -- the screen to apply the wallpaper, as seen in gears.wallpaper functions
screens = nil, -- an array of screens to apply the wallpaper on. If 'screen' is also provided, this is overridden
change_timer = nil, -- the timer in seconds. If set, call the set_function every change_timer seconds
set_function = nil, -- the setter function
-- parameters used by bling.module.wallpaper.prepare_list
wallpaper = nil, -- the wallpaper object, see simple or simple_schedule documentation
image_formats = {"jpg", "jpeg", "png", "bmp"}, -- when searching in folder, consider these files only
recursive = true, -- when searching in folder, search also in subfolders
-- parameters used by bling.module.wallpaper.apply
position = nil, -- use a function of gears.wallpaper when applicable ("centered", "fit", "maximized", "tiled")
background = beautiful.bg_normal or "black", -- see gears.wallpaper functions
ignore_aspect = false, -- see gears.wallpaper.maximized
offset = {x = 0, y = 0}, -- see gears.wallpaper functions
scale = 1, -- see gears.wallpaper.centered
-- parameters that only apply to bling.module.wallpaper.setter.awesome (as a setter or as a wallpaper function)
colors = { -- see beautiful.theme_assets.wallpaper
bg = beautiful.bg_color, -- the actual default is this color but darkened or lightned
fg = beautiful.fg_color,
alt_fg = beautiful.fg_focus
}
}
```
Check documentation in [module/wallpaper.lua](module/wallpaper.lua) for more details.

View File

@ -0,0 +1,240 @@
## 🎵 Playerctl <!-- {docsify-ignore} -->
This is a signal module in which you can connect to certain bling signals to grab playerctl info. Currently, this is what it supports:
- Song title and artist
- Album art (the path this module downloaded the art to)
- If playing or not
- Position
- Song length
- If there are no players on
This module relies on `playerctl` and `curl`. If you have this module disabled, you won't need those programs. With this module, you can create a widget like below without worrying about the backend.
![](https://user-images.githubusercontent.com/33443763/107377569-fa807900-6a9f-11eb-93c1-174c58eb7bf1.png)
*screenshot by [javacafe](https://github.com/JavaCafe01)*
### Usage
To enable: `playerctl = bling.signal.playerctl.lib/cli()`
To disable: `playerctl:disable()`
#### Playerctl_lib Signals
**Note**: When connecting to signals with the new `playerctl` module, the object itself is always given to you as the first parameter.
```lua
-- metadata
-- title (string)
-- artist (string)
-- album_path (string)
-- album (string)
-- new (bool)
-- player_name (string)
-- position
-- interval_sec (number)
-- length_sec (number)
-- player_name (string)
-- playback_status
-- playing (boolean)
-- player_name (string)
-- seeked
-- position (number)
-- player_name (string)
-- volume
-- volume (number)
-- player_name (string)
-- loop_status
-- loop_status (string)
-- player_name (string)
-- shuffle
-- shuffle (boolean)
-- player_name (string)
-- exit
-- player_name (string)
-- no_players
-- (No parameters)
```
#### Playerctl_cli Signals
```lua
-- metadata
-- title (string)
-- artist (string)
-- album_path (string)
-- album (string)
-- player_name (string)
-- position
-- interval_sec (number)
-- length_sec (number)
-- playback_status
-- playing (boolean)
-- volume
-- volume (number)
-- loop_status
-- loop_status (string)
-- shuffle
-- shuffle (bool)
-- no_players
-- (No parameters)
```
#### Playerctl Functions
With this library we also give the user a way to interact with Playerctl, such as playing, pausing, seeking, etc.
Here are the functions provided:
```lua
-- disable()
-- pause(player)
-- play(player)
-- stop(player)
-- play_pause(player)
-- previous(player)
-- next(player)
-- set_loop_status(loop_status, player)
-- cycle_loop_status(player)
-- set_position(position, player)
-- set_shuffle(shuffle, player)
-- cycle_shuffle(player)
-- set_volume(volume, player)
-- get_manager()
-- get_active_player()
-- get_player_of_name(name)
```
### Example Implementation
Lets say we have an imagebox. If I wanted to set the imagebox to show the album art, all I have to do is this:
```lua
local art = wibox.widget {
image = "default_image.png",
resize = true,
forced_height = dpi(80),
forced_width = dpi(80),
widget = wibox.widget.imagebox
}
local name_widget = wibox.widget {
markup = 'No players',
align = 'center',
valign = 'center',
widget = wibox.widget.textbox
}
local title_widget = wibox.widget {
markup = 'Nothing Playing',
align = 'center',
valign = 'center',
widget = wibox.widget.textbox
}
local artist_widget = wibox.widget {
markup = 'Nothing Playing',
align = 'center',
valign = 'center',
widget = wibox.widget.textbox
}
-- Get Song Info
local playerctl = bling.signal.playerctl.lib()
playerctl:connect_signal("metadata",
function(_, title, artist, album_path, album, new, player_name)
-- Set art widget
art:set_image(gears.surface.load_uncached(album_path))
-- Set player name, title and artist widgets
name_widget:set_markup_silently(player_name)
title_widget:set_markup_silently(title)
artist_widget:set_markup_silently(artist)
end)
```
Thats all! You don't even have to worry about updating the widgets, the signals will handle that for you.
Here's another example in which you get a notification with the album art, title, and artist whenever the song changes.
```lua
local naughty = require("naughty")
local playerctl = bling.signal.playerctl.lib()
playerctl:connect_signal("metadata",
function(_, title, artist, album_path, album, new, player_name)
if new == true then
naughty.notify({title = title, text = artist, image = album_path})
end
end)
```
We can also link a playerctl function to a button click!
```lua
local playerctl = bling.signal.playerctl.lib()
button:buttons(gears.table.join(
awful.button({}, 1, function()
playerctl:play_pause()
end)
))
```
### Theme Variables and Configuration
By default, this module will output signals from the most recently active player. If you wish to customize the behavior furthur, the following configuration options are available depending on the selected backend. Here is a summary of the two backends and which configuration options they support.
| Option | playerctl_cli | playerctl_lib |
| ------------------- | ------------------ | ------------------ |
| ignore | :heavy_check_mark: | :heavy_check_mark: |
| player | :heavy_check_mark: | :heavy_check_mark: |
| update_on_activity | | :heavy_check_mark: |
| interval | :heavy_check_mark: | :heavy_check_mark: |
| debounce_delay | :heavy_check_mark: | :heavy_check_mark: |
- `ignore`: This option is either a string with a single name or a table of strings containing names of players that will be ignored by this module. It is empty by default.
- `player`: This option is either a string with a single name or a table of strings containing names of players this module will emit signals for. It also acts as a way to prioritize certain players over others with players listed earlier in the table being preferred over players listed later. The special name `%any` can be used once to match any player not found in the list. It is empty by default.
- `update_on_activity`: This option is a boolean that, when true, will cause the module to output signals from the most recently active player while still adhering to the player priority specified with the `player` option. If `false`, the module will output signals from the player that started first, again, while still adhering to the player priority. It is `true` by default.
- `interval`: This option is a number specifying the update interval for fetching the player position. It is 1 by default.
- `debounce_delay`: This option is a number specifying the debounce timer interval. If a new metadata signal gets emitted before debounce_delay has passed, the last signal will be dropped.
This is to help with some players sending multiple signals. It is `0.35` by default.
These options can be set through a call to `bling.signal.playerctl.lib/cli()` or these theme variables:
```lua
theme.playerctl_ignore = {}
theme.playerctl_player = {}
theme.playerctl_update_on_activity = true
theme.playerctl_position_update_interval = 1
```
#### Example Configurations
```lua
-- Prioritize ncspot over all other players and ignore firefox players (e.g. YouTube and Twitch tabs) completely
playerctl = bling.signal.playerctl.lib {
ignore = "firefox",
player = {"ncspot", "%any"}
}
-- OR in your theme file:
-- Same config as above but with theme variables
theme.playerctl_ignore = "firefox"
theme.playerctl_player = {"ncspot", "%any"}
-- Prioritize vlc over all other players and deprioritize spotify
theme.playerctl_backend = "playerctl_lib"
theme.playerctl_player = {"vlc", "%any", "spotify"}
-- Disable priority of most recently active players
theme.playerctl_update_on_activity = false
-- Only emit the position signal every 2 seconds
theme.playerctl_position_update_interval = 2
```

View File

@ -0,0 +1,117 @@
```lua
--[[ Bling theme variables template
This file has all theme variables of the bling module.
Every variable has a small comment on what it does.
You might just want to copy that whole part into your theme.lua and start adjusting from there.
--]]
-- window swallowing
theme.dont_swallow_classname_list = {"firefox", "Gimp"} -- list of class names that should not be swallowed
theme.dont_swallow_filter_activated = true -- whether the filter above should be active
-- flash focus
theme.flash_focus_start_opacity = 0.6 -- the starting opacity
theme.flash_focus_step = 0.01 -- the step of animation
-- playerctl signal
theme.playerctl_backend = "playerctl_cli" -- backend to use
theme.playerctl_ignore = {} -- list of players to be ignored
theme.playerctl_player = {} -- list of players to be used in priority order
theme.playerctl_update_on_activity = true -- whether to prioritize the most recently active players or not
theme.playerctl_position_update_interval = 1 -- the update interval for fetching the position from playerctl
-- tabbed
theme.tabbed_spawn_in_tab = false -- whether a new client should spawn into the focused tabbing container
-- tabbar general
theme.tabbar_ontop = false
theme.tabbar_radius = 0 -- border radius of the tabbar
theme.tabbar_style = "default" -- style of the tabbar ("default", "boxes" or "modern")
theme.tabbar_font = "Sans 11" -- font of the tabbar
theme.tabbar_size = 40 -- size of the tabbar
theme.tabbar_position = "top" -- position of the tabbar
theme.tabbar_bg_normal = "#000000" -- background color of the focused client on the tabbar
theme.tabbar_fg_normal = "#ffffff" -- foreground color of the focused client on the tabbar
theme.tabbar_bg_focus = "#1A2026" -- background color of unfocused clients on the tabbar
theme.tabbar_fg_focus = "#ff0000" -- foreground color of unfocused clients on the tabbar
theme.tabbar_bg_focus_inactive = nil -- background color of the focused client on the tabbar when inactive
theme.tabbar_fg_focus_inactive = nil -- foreground color of the focused client on the tabbar when inactive
theme.tabbar_bg_normal_inactive = nil -- background color of unfocused clients on the tabbar when inactive
theme.tabbar_fg_normal_inactive = nil -- foreground color of unfocused clients on the tabbar when inactive
theme.tabbar_disable = false -- disable the tab bar entirely
-- mstab
theme.mstab_bar_disable = false -- disable the tabbar
theme.mstab_bar_ontop = false -- whether you want to allow the bar to be ontop of clients
theme.mstab_dont_resize_slaves = false -- whether the tabbed stack windows should be smaller than the
-- currently focused stack window (set it to true if you use
-- transparent terminals. False if you use shadows on solid ones
theme.mstab_bar_padding = "default" -- how much padding there should be between clients and your tabbar
-- by default it will adjust based on your useless gaps.
-- If you want a custom value. Set it to the number of pixels (int)
theme.mstab_border_radius = 0 -- border radius of the tabbar
theme.mstab_bar_height = 40 -- height of the tabbar
theme.mstab_tabbar_position = "top" -- position of the tabbar (mstab currently does not support left,right)
theme.mstab_tabbar_style = "default" -- style of the tabbar ("default", "boxes" or "modern")
-- defaults to the tabbar_style so only change if you want a
-- different style for mstab and tabbed
-- the following variables are currently only for the "modern" tabbar style
theme.tabbar_color_close = "#f9929b" -- chnges the color of the close button
theme.tabbar_color_min = "#fbdf90" -- chnges the color of the minimize button
theme.tabbar_color_float = "#ccaced" -- chnges the color of the float button
-- tag preview widget
theme.tag_preview_widget_border_radius = 0 -- Border radius of the widget (With AA)
theme.tag_preview_client_border_radius = 0 -- Border radius of each client in the widget (With AA)
theme.tag_preview_client_opacity = 0.5 -- Opacity of each client
theme.tag_preview_client_bg = "#000000" -- The bg color of each client
theme.tag_preview_client_border_color = "#ffffff" -- The border color of each client
theme.tag_preview_client_border_width = 3 -- The border width of each client
theme.tag_preview_widget_bg = "#000000" -- The bg color of the widget
theme.tag_preview_widget_border_color = "#ffffff" -- The border color of the widget
theme.tag_preview_widget_border_width = 3 -- The border width of the widget
theme.tag_preview_widget_margin = 0 -- The margin of the widget
-- task preview widget
theme.task_preview_widget_border_radius = 0 -- Border radius of the widget (With AA)
theme.task_preview_widget_bg = "#000000" -- The bg color of the widget
theme.task_preview_widget_border_color = "#ffffff" -- The border color of the widget
theme.task_preview_widget_border_width = 3 -- The border width of the widget
theme.task_preview_widget_margin = 0 -- The margin of the widget
-- tabbed misc widget(s)
theme.bling_tabbed_misc_titlebar_indicator = {
layout_spacing = dpi(4),
icon_size = dpi(20),
icon_margin = dpi(4),
bg_color_focus = "#ff0000",
bg_color = "#00000000",
icon_shape = function(cr, w, h)
gears.shape.rounded_rect(cr, w, h, 0)
end,
layout = wibox.layout.fixed.horizontal
}
-- window switcher widget
theme.window_switcher_widget_bg = "#000000" -- The bg color of the widget
theme.window_switcher_widget_border_width = 3 -- The border width of the widget
theme.window_switcher_widget_border_radius = 0 -- The border radius of the widget
theme.window_switcher_widget_border_color = "#ffffff" -- The border color of the widget
theme.window_switcher_clients_spacing = 20 -- The space between each client item
theme.window_switcher_client_icon_horizontal_spacing = 5 -- The space between client icon and text
theme.window_switcher_client_width = 150 -- The width of one client widget
theme.window_switcher_client_height = 250 -- The height of one client widget
theme.window_switcher_client_margins = 10 -- The margin between the content and the border of the widget
theme.window_switcher_thumbnail_margins = 10 -- The margin between one client thumbnail and the rest of the widget
theme.thumbnail_scale = false -- If set to true, the thumbnails fit policy will be set to "fit" instead of "auto"
theme.window_switcher_name_margins = 10 -- The margin of one clients title to the rest of the widget
theme.window_switcher_name_valign = "center" -- How to vertically align one clients title
theme.window_switcher_name_forced_width = 200 -- The width of one title
theme.window_switcher_name_font = "sans 11" -- The font of all titles
theme.window_switcher_name_normal_color = "#ffffff" -- The color of one title if the client is unfocused
theme.window_switcher_name_focus_color = "#ff0000" -- The color of one title if the client is focused
theme.window_switcher_icon_valign = "center" -- How to vertically align the one icon
theme.window_switcher_icon_width = 40 -- The width of one icon
```

View File

@ -0,0 +1,117 @@
## 🧱 Tabbed Miscellaneous <!-- {docsify-ignore} -->
This comprises a few widgets to better represent tabbed groups (from the tabbed module) in your desktop.
The widgets currently included are:
- Titlebar Indicator
- Tasklist
![Preview Image](https://i.imgur.com/ZeYSrxY.png)
## Titlebar Indicator
### Usage
To use the task list indicator:
**NOTE:** Options can be set as theme vars under the table `theme.bling_tabbed_misc_titlebar_indicator`
```lua
bling.widget.tabbed_misc.titlebar_indicator(client, {
layout = wibox.layout.fixed.vertical,
layout_spacing = dpi(5), -- Set spacing in between items
icon_size = dpi(24), -- Set icon size
icon_margin = 0, -- Set icon margin
fg_color = "#cccccc", -- Normal color for text
fg_color_focus = "#ffffff", -- Color for focused text
bg_color_focus = "#282828", -- Color for the focused items
bg_color = "#1d2021", -- Color for normal / unfocused items
icon_shape = gears.shape.circle -- Set icon shape,
})
```
a widget_template option is also available:
```lua
bling.widget.tabbed_misc.titlebar_indicator(client, {
widget_template = {
{
widget = awful.widget.clienticon,
id = 'icon_role'
},
widget = wibox.container.margin,
margins = 2,
id = 'bg_role',
update_callback = function(self, client, group)
if client == group.clients[group.focused_idx] then
self.margins = 5
end
end
}
})
```
### Example Implementation
You normally embed the widget in your titlebar...
```lua
awful.titlebar(c).widget = {
{ -- Left
bling.widget.tabbed_misc.titlebar_indicator(c),
layout = wibox.layout.fixed.horizontal
},
{ -- Middle
{ -- Title
align = "center",
widget = awful.titlebar.widget.titlewidget(c)
},
buttons = buttons,
layout = wibox.layout.flex.horizontal
},
{ -- Right
awful.titlebar.widget.maximizedbutton(c),
awful.titlebar.widget.closebutton (c),
layout = wibox.layout.fixed.horizontal
},
layout = wibox.layout.align.horizontal
}
```
## Tasklist
The module exports a function that can be added to your tasklist as a `update_callback`
### Usage
```lua
awful.widget.tasklist({
screen = s,
filter = awful.widget.tasklist.filter.currenttags,
layout = {
spacing = dpi(10),
layout = wibox.layout.fixed.vertical,
},
style = {
bg_normal = "#00000000",
},
widget_template = {
{
{
widget = wibox.widget.imagebox,
id = "icon_role",
align = "center",
valign = "center",
},
width = dpi(24),
height = dpi(24),
widget = wibox.container.constraint,
},
widget = wibox.container.background, -- IT MUST BE A CONTAINER WIDGET AS THAT IS WHAT THE FUNCTION EXPECTS
update_callback = require("bling.widget.tabbed_misc").custom_tasklist,
id = "background_role",
},
})
```
If you need to do something else, it can be used like so
```lua
update_callback = function(self, client, index, clients)
require("bling.widget.tabbed_misc").custom_tasklist(self, client, index, clients)
require("naughty").notify({ text = "Tasklist was updated" })
end
```

View File

@ -0,0 +1,155 @@
## 🔍 Tag Preview <!-- {docsify-ignore} -->
This is a popup widget that will show a preview of a specified tag that illustrates the position, size, content, and icon of all clients.
![](https://imgur.com/zFdvs4K.gif)
*gif by [javacafe](https://github.com/JavaCafe01)*
### Usage
To enable:
```lua
bling.widget.tag_preview.enable {
show_client_content = false, -- Whether or not to show the client content
x = 10, -- The x-coord of the popup
y = 10, -- The y-coord of the popup
scale = 0.25, -- The scale of the previews compared to the screen
honor_padding = false, -- Honor padding when creating widget size
honor_workarea = false, -- Honor work area when creating widget size
placement_fn = function(c) -- Place the widget using awful.placement (this overrides x & y)
awful.placement.top_left(c, {
margins = {
top = 30,
left = 30
}
})
end,
background_widget = wibox.widget { -- Set a background image (like a wallpaper) for the widget
image = beautiful.wallpaper,
horizontal_fit_policy = "fit",
vertical_fit_policy = "fit",
widget = wibox.widget.imagebox
}
}
```
Here are the signals available:
```lua
-- bling::tag_preview::update -- first line is the signal
-- t (tag) -- indented lines are function parameters
-- bling::tag_preview::visibility
-- s (screen)
-- v (boolean)
```
By default, the widget is not visible. You must implement when it will update and when it will show.
### Example Implementation
We can trigger the widget to show the specific tag when hovering over it in the taglist. The code shown below is the example taglist from the [AwesomeWM docs](https://awesomewm.org/doc/api/classes/awful.widget.taglist.html). Basically, we are going to update the widget and toggle it through the taglist's `create_callback`. (The bling addons are commented)
```lua
s.mytaglist = awful.widget.taglist {
screen = s,
filter = awful.widget.taglist.filter.all,
style = {
shape = gears.shape.powerline
},
layout = {
spacing = -12,
spacing_widget = {
color = '#dddddd',
shape = gears.shape.powerline,
widget = wibox.widget.separator,
},
layout = wibox.layout.fixed.horizontal
},
widget_template = {
{
{
{
{
{
id = 'index_role',
widget = wibox.widget.textbox,
},
margins = 4,
widget = wibox.container.margin,
},
bg = '#dddddd',
shape = gears.shape.circle,
widget = wibox.container.background,
},
{
{
id = 'icon_role',
widget = wibox.widget.imagebox,
},
margins = 2,
widget = wibox.container.margin,
},
{
id = 'text_role',
widget = wibox.widget.textbox,
},
layout = wibox.layout.fixed.horizontal,
},
left = 18,
right = 18,
widget = wibox.container.margin
},
id = 'background_role',
widget = wibox.container.background,
-- Add support for hover colors and an index label
create_callback = function(self, c3, index, objects) --luacheck: no unused args
self:get_children_by_id('index_role')[1].markup = '<b> '..index..' </b>'
self:connect_signal('mouse::enter', function()
-- BLING: Only show widget when there are clients in the tag
if #c3:clients() > 0 then
-- BLING: Update the widget with the new tag
awesome.emit_signal("bling::tag_preview::update", c3)
-- BLING: Show the widget
awesome.emit_signal("bling::tag_preview::visibility", s, true)
end
if self.bg ~= '#ff0000' then
self.backup = self.bg
self.has_backup = true
end
self.bg = '#ff0000'
end)
self:connect_signal('mouse::leave', function()
-- BLING: Turn the widget off
awesome.emit_signal("bling::tag_preview::visibility", s, false)
if self.has_backup then self.bg = self.backup end
end)
end,
update_callback = function(self, c3, index, objects) --luacheck: no unused args
self:get_children_by_id('index_role')[1].markup = '<b> '..index..' </b>'
end,
},
buttons = taglist_buttons
}
```
### Theme Variables
```lua
theme.tag_preview_widget_border_radius = 0 -- Border radius of the widget (With AA)
theme.tag_preview_client_border_radius = 0 -- Border radius of each client in the widget (With AA)
theme.tag_preview_client_opacity = 0.5 -- Opacity of each client
theme.tag_preview_client_bg = "#000000" -- The bg color of each client
theme.tag_preview_client_border_color = "#ffffff" -- The border color of each client
theme.tag_preview_client_border_width = 3 -- The border width of each client
theme.tag_preview_widget_bg = "#000000" -- The bg color of the widget
theme.tag_preview_widget_border_color = "#ffffff" -- The border color of the widget
theme.tag_preview_widget_border_width = 3 -- The border width of the widget
theme.tag_preview_widget_margin = 0 -- The margin of the widget
```
NOTE: I recommend to only use the widget border radius theme variable when not using shadows with a compositor, as anti-aliased rounding with the outer widgets made with AwesomeWM rely on the actual bg being transparent. If you want rounding with shadows on the widget, use a compositor like [jonaburg's fork](https://github.com/jonaburg/picom).

View File

@ -0,0 +1,152 @@
## 🔍 Task Preview <!-- {docsify-ignore} -->
This is a popup widget that will show a preview of the specified client. It is supposed to mimic the small popup that Windows has when hovering over the application icon.
![](https://user-images.githubusercontent.com/33443763/124705653-d7b98b80-deaa-11eb-8091-42bbe62365be.png)
*image by [javacafe](https://github.com/JavaCafe01)*
### Usage
To enable:
```lua
bling.widget.task_preview.enable {
x = 20, -- The x-coord of the popup
y = 20, -- The y-coord of the popup
height = 200, -- The height of the popup
width = 200, -- The width of the popup
placement_fn = function(c) -- Place the widget using awful.placement (this overrides x & y)
awful.placement.bottom(c, {
margins = {
bottom = 30
}
})
end
}
```
To allow for more customization, there is also a `widget_structure` property (as seen in some default awesome widgets) which is optional. An example is as follows -
```lua
bling.widget.task_preview.enable {
x = 20, -- The x-coord of the popup
y = 20, -- The y-coord of the popup
height = 200, -- The height of the popup
width = 200, -- The width of the popup
placement_fn = function(c) -- Place the widget using awful.placement (this overrides x & y)
awful.placement.bottom(c, {
margins = {
bottom = 30
}
})
end,
-- Your widget will automatically conform to the given size due to a constraint container.
widget_structure = {
{
{
{
id = 'icon_role',
widget = awful.widget.clienticon, -- The client icon
},
{
id = 'name_role', -- The client name / title
widget = wibox.widget.textbox,
},
layout = wibox.layout.flex.horizontal
},
widget = wibox.container.margin,
margins = 5
},
{
id = 'image_role', -- The client preview
resize = true,
valign = 'center',
halign = 'center',
widget = wibox.widget.imagebox,
},
layout = wibox.layout.fixed.vertical
}
}
```
Here are the signals available:
```lua
-- bling::task_preview::visibility -- first line is the signal
-- s (screen) -- indented lines are function parameters
-- v (boolean)
-- c (client)
```
By default, the widget is not visible. You must implement when it will update and when it will show.
### Example Implementation
We can trigger the widget to show the specific client when hovering over it in the tasklist. The code shown below is the example icon only tasklist from the [AwesomeWM docs](https://awesomewm.org/doc/api/classes/awful.widget.tasklist.html). Basically, we are going to toggle the widget through the tasklist's `create_callback`. (The bling addons are commented)
```lua
s.mytasklist = awful.widget.tasklist {
screen = s,
filter = awful.widget.tasklist.filter.currenttags,
buttons = tasklist_buttons,
layout = {
spacing_widget = {
{
forced_width = 5,
forced_height = 24,
thickness = 1,
color = '#777777',
widget = wibox.widget.separator
},
valign = 'center',
halign = 'center',
widget = wibox.container.place,
},
spacing = 1,
layout = wibox.layout.fixed.horizontal
},
-- Notice that there is *NO* wibox.wibox prefix, it is a template,
-- not a widget instance.
widget_template = {
{
wibox.widget.base.make_widget(),
forced_height = 5,
id = 'background_role',
widget = wibox.container.background,
},
{
{
id = 'clienticon',
widget = awful.widget.clienticon,
},
margins = 5,
widget = wibox.container.margin
},
nil,
create_callback = function(self, c, index, objects) --luacheck: no unused args
self:get_children_by_id('clienticon')[1].client = c
-- BLING: Toggle the popup on hover and disable it off hover
self:connect_signal('mouse::enter', function()
awesome.emit_signal("bling::task_preview::visibility", s,
true, c)
end)
self:connect_signal('mouse::leave', function()
awesome.emit_signal("bling::task_preview::visibility", s,
false, c)
end)
end,
layout = wibox.layout.align.vertical,
},
}
```
### Theme Variables
```lua
theme.task_preview_widget_border_radius = 0 -- Border radius of the widget (With AA)
theme.task_preview_widget_bg = "#000000" -- The bg color of the widget
theme.task_preview_widget_border_color = "#ffffff" -- The border color of the widget
theme.task_preview_widget_border_width = 3 -- The border width of the widget
theme.task_preview_widget_margin = 0 -- The margin of the widget
```
NOTE: I recommend to only use the widget border radius theme variable when not using shadows with a compositor, as anti-aliased rounding with the outer widgets made with AwesomeWM rely on the actual bg being transparent. If you want rounding with shadows on the widget, use a compositor like [jonaburg's fork](https://github.com/jonaburg/picom).

View File

@ -0,0 +1,67 @@
## 🎨 Window Switcher <!-- {docsify-ignore} -->
A popup with client previews that allows you to switch clients similar to the alt-tab menu in MacOS, GNOME, and Windows.
![](https://user-images.githubusercontent.com/70270606/133311802-8aef1012-346f-4f4c-843d-10d9de54ffeb.png)
*image by [No37](https://github.com/Nooo37)*
### Usage
To enable:
```lua
bling.widget.window_switcher.enable {
type = "thumbnail", -- set to anything other than "thumbnail" to disable client previews
-- keybindings (the examples provided are also the default if kept unset)
hide_window_switcher_key = "Escape", -- The key on which to close the popup
minimize_key = "n", -- The key on which to minimize the selected client
unminimize_key = "N", -- The key on which to unminimize all clients
kill_client_key = "q", -- The key on which to close the selected client
cycle_key = "Tab", -- The key on which to cycle through all clients
previous_key = "Left", -- The key on which to select the previous client
next_key = "Right", -- The key on which to select the next client
vim_previous_key = "h", -- Alternative key on which to select the previous client
vim_next_key = "l", -- Alternative key on which to select the next client
cycleClientsByIdx = awful.client.focus.byidx, -- The function to cycle the clients
filterClients = awful.widget.tasklist.filter.currenttags, -- The function to filter the viewed clients
}
```
To run the window swicher you have to emit this signal from within your configuration (usually using a keybind).
```lua
awesome.emit_signal("bling::window_switcher::turn_on")
```
For example:
```lua
awful.key({Mod1}, "Tab", function()
awesome.emit_signal("bling::window_switcher::turn_on")
end, {description = "Window Switcher", group = "bling"})
```
### Theme Variables
```lua
theme.window_switcher_widget_bg = "#000000" -- The bg color of the widget
theme.window_switcher_widget_border_width = 3 -- The border width of the widget
theme.window_switcher_widget_border_radius = 0 -- The border radius of the widget
theme.window_switcher_widget_border_color = "#ffffff" -- The border color of the widget
theme.window_switcher_clients_spacing = 20 -- The space between each client item
theme.window_switcher_client_icon_horizontal_spacing = 5 -- The space between client icon and text
theme.window_switcher_client_width = 150 -- The width of one client widget
theme.window_switcher_client_height = 250 -- The height of one client widget
theme.window_switcher_client_margins = 10 -- The margin between the content and the border of the widget
theme.window_switcher_thumbnail_margins = 10 -- The margin between one client thumbnail and the rest of the widget
theme.thumbnail_scale = false -- If set to true, the thumbnails fit policy will be set to "fit" instead of "auto"
theme.window_switcher_name_margins = 10 -- The margin of one clients title to the rest of the widget
theme.window_switcher_name_valign = "center" -- How to vertically align one clients title
theme.window_switcher_name_forced_width = 200 -- The width of one title
theme.window_switcher_name_font = "sans 11" -- The font of all titles
theme.window_switcher_name_normal_color = "#ffffff" -- The color of one title if the client is unfocused
theme.window_switcher_name_focus_color = "#ff0000" -- The color of one title if the client is focused
theme.window_switcher_icon_valign = "center" -- How to vertically align the one icon
theme.window_switcher_icon_width = 40 -- The width of one icon
```

View File

@ -0,0 +1,142 @@
local lgi = require("lgi")
local Gio = lgi.Gio
local Gtk = lgi.require("Gtk", "3.0")
local gobject = require("gears.object")
local gtable = require("gears.table")
local setmetatable = setmetatable
local ipairs = ipairs
local icon_theme = { mt = {} }
local name_lookup =
{
["jetbrains-studio"] = "android-studio"
}
local function get_icon_by_pid_command(self, client, apps)
local pid = client.pid
if pid ~= nil then
local handle = io.popen(string.format("ps -p %d -o comm=", pid))
local pid_command = handle:read("*a"):gsub("^%s*(.-)%s*$", "%1")
handle:close()
for _, app in ipairs(apps) do
local executable = app:get_executable()
if executable and executable:find(pid_command, 1, true) then
return self:get_gicon_path(app:get_icon())
end
end
end
end
local function get_icon_by_icon_name(self, client, apps)
local icon_name = client.icon_name and client.icon_name:lower() or nil
if icon_name ~= nil then
for _, app in ipairs(apps) do
local name = app:get_name():lower()
if name and name:find(icon_name, 1, true) then
return self:get_gicon_path(app:get_icon())
end
end
end
end
local function get_icon_by_class(self, client, apps)
if client.class ~= nil then
local class = name_lookup[client.class] or client.class:lower()
-- Try to remove dashes
local class_1 = class:gsub("[%-]", "")
-- Try to replace dashes with dot
local class_2 = class:gsub("[%-]", ".")
-- Try to match only the first word
local class_3 = class:match("(.-)-") or class
class_3 = class_3:match("(.-)%.") or class_3
class_3 = class_3:match("(.-)%s+") or class_3
local possible_icon_names = { class, class_3, class_2, class_1 }
for _, app in ipairs(apps) do
local id = app:get_id():lower()
for _, possible_icon_name in ipairs(possible_icon_names) do
if id and id:find(possible_icon_name, 1, true) then
return self:get_gicon_path(app:get_icon())
end
end
end
end
end
function icon_theme:get_client_icon_path(client)
local apps = Gio.AppInfo.get_all()
return get_icon_by_pid_command(self, client, apps) or
get_icon_by_icon_name(self, client, apps) or
get_icon_by_class(self, client, apps) or
client.icon or
self:choose_icon({"window", "window-manager", "xfwm4-default", "window_list" })
end
function icon_theme:choose_icon(icons_names)
local icon_info = self.gtk_theme:choose_icon(icons_names, self.icon_size, 0);
if icon_info then
local icon_path = icon_info:get_filename()
if icon_path then
return icon_path
end
end
return ""
end
function icon_theme:get_gicon_path(gicon)
if gicon == nil then
return ""
end
local icon_info = self.gtk_theme:lookup_by_gicon(gicon, self.icon_size, 0);
if icon_info then
local icon_path = icon_info:get_filename()
if icon_path then
return icon_path
end
end
return ""
end
function icon_theme:get_icon_path(icon_name)
local icon_info = self.gtk_theme:lookup_icon(icon_name, self.icon_size, 0)
if icon_info then
local icon_path = icon_info:get_filename()
if icon_path then
return icon_path
end
end
return ""
end
local function new(theme_name, icon_size)
local ret = gobject{}
gtable.crush(ret, icon_theme, true)
ret.name = theme_name or nil
ret.icon_size = icon_size or 48
if theme_name then
ret.gtk_theme = Gtk.IconTheme.new()
Gtk.IconTheme.set_custom_theme(ret.gtk_theme, theme_name);
else
ret.gtk_theme = Gtk.IconTheme.get_default()
end
return ret
end
function icon_theme.mt:__call(...)
return new(...)
end
return setmetatable(icon_theme, icon_theme.mt)

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Some files were not shown because too many files have changed in this diff Show More