├── LICENSE
├── autostart.lua
├── bar
├── init.lua
└── modules
│ ├── battery.lua
│ ├── brightness.lua
│ ├── lain_battery.lua
│ ├── lain_mpd.lua
│ ├── mpd.lua
│ ├── systray.lua
│ ├── taglist.lua
│ ├── time.lua
│ └── volume.lua
├── external
└── fancy_taglist.lua
├── helper.lua
├── keys.lua
├── notifications.lua
├── rc.lua
├── rules.lua
├── theme
├── gtk.lua
└── init.lua
└── wallpaper.lua
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 sachnr
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/autostart.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful.init")
2 |
3 | -- reload processes on config reload
4 | awful.spawn.easy_async_with_shell(
5 | 'ps x | grep "blueman-applet" | grep -v grep | awk "{print $1}" | xargs kill',
6 | function()
7 | awful.spawn("blueman-applet")
8 | end
9 | )
10 |
11 | awful.spawn.easy_async_with_shell('ps x | grep "nm-applet" | grep -v grep | awk "{print $1}" | xargs kill', function()
12 | awful.spawn("nm-applet --indicator")
13 | end)
14 | --
15 | awful.spawn.easy_async_with_shell('ps x | grep "pasystray" | grep -v grep | awk "{print $1}" | xargs kill', function()
16 | awful.spawn("pasystray")
17 | end)
18 |
19 | -- awful.spawn("xrandr --output HDMI-0 --primary --mode 1280x960 --rate 144")
20 | -- awful.spawn("xrandr --output HDMI-0 --primary --mode 1920x1080 --rate 144")
21 | -- awful.spawn("xrandr --output DP-0 --primary --mode 1920x1080 --rate 144")
22 | --
23 | awful.spawn("nvidia-settings --load-config-only")
24 | awful.spawn("autorandr --load desktop")
25 |
26 | awful.spawn.easy_async_with_shell(
27 | 'ps x | grep "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1" | grep -v grep | awk "{print $1}" | xargs kill',
28 | function()
29 | awful.spawn("/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1")
30 | end
31 | )
32 |
33 | awful.spawn.easy_async_with_shell('ps x | grep "picom" | grep -v grep | awk "{print $1}" | xargs kill', function()
34 | awful.spawn("picom")
35 | end)
36 |
37 | awful.spawn("xset r rate 560 50")
38 |
--------------------------------------------------------------------------------
/bar/init.lua:
--------------------------------------------------------------------------------
1 | local wibox = require("wibox")
2 | local beautiful = require("beautiful")
3 | local systray = require("bar.modules.systray")
4 | local dpi = beautiful.xresources.apply_dpi
5 | local awful = require("awful")
6 | local naughty = require("naughty")
7 |
8 | -- Table of layouts to cover with awful.layout.inc, order matters.
9 | ---@diagnostic disable-next-line: undefined-global
10 | tag.connect_signal("request::default_layouts", function()
11 | awful.layout.append_default_layouts({
12 | awful.layout.suit.tile,
13 | awful.layout.suit.floating,
14 | })
15 | end)
16 |
17 | local tags = {}
18 | local bar = {}
19 |
20 | bar.setup = function(args)
21 | setmetatable(args, { __index = { style = "horizontal" } })
22 |
23 | local fancy_taglist = require("bar.modules.taglist")
24 | local time = require("bar.modules.time")
25 | local volume = require("bar.modules.volume")
26 | local mpd_text = require("bar.modules.mpd")
27 | local brightness = require("bar.modules.brightness")
28 | local battery = require("bar.modules.battery")
29 |
30 | require("bar.modules.lain_mpd")()
31 | require("bar.modules.lain_battery")({
32 | battery = "BAT0",
33 | notify = true,
34 | })
35 |
36 | ---@diagnostic disable-next-line: undefined-global
37 | awful.screen.connect_for_each_screen(function(s)
38 | tags[s] = awful.tag({ "1", "2", "3", "4", "5", "6", "7", "8", "9" }, s, awful.layout.layouts[1])
39 |
40 | s.mywibox = awful.wibar({
41 | position = "bottom",
42 | screen = s,
43 | height = dpi(28),
44 | widget = {
45 | layout = wibox.layout.align.horizontal,
46 | expand = "none",
47 | { -- Left widgets
48 | fancy_taglist.setup(s),
49 | layout = wibox.layout.fixed.horizontal,
50 | },
51 | { -- Middle widget
52 | mpd_text.setup(),
53 | layout = wibox.layout.flex.horizontal,
54 | },
55 | { -- Right widgets
56 | (_G.laptop and battery.setup({}) or nil),
57 | (_G.laptop and brightness.setup({}) or nil),
58 | volume.setup(),
59 | time.setup(),
60 | systray.setup(),
61 | layout = wibox.layout.fixed.horizontal,
62 | },
63 | },
64 | })
65 | end)
66 | end
67 |
68 | return bar
69 |
--------------------------------------------------------------------------------
/bar/modules/battery.lua:
--------------------------------------------------------------------------------
1 | local wibox = require("wibox.init")
2 | local beautiful = require("beautiful")
3 | local dpi = beautiful.xresources.apply_dpi
4 | local helper = require("helper")
5 |
6 | local M = {}
7 |
8 | local timeout = 2
9 |
10 | M.icons = {
11 | crit = "",
12 | low = "",
13 | med = "",
14 | high = "",
15 | full = "",
16 | }
17 |
18 | local function battery_update()
19 | local icon = M.icons.full
20 | if bat_now.perc <= 20 then
21 | icon = M.icons.crit
22 | elseif bat_now.perc <= 40 then
23 | icon = M.icons.low
24 | elseif bat_now.perc <= 60 then
25 | icon = M.icons.med
26 | elseif bat_now.perc <= 80 then
27 | icon = M.icons.high
28 | elseif bat_now.perc <= 100 then
29 | icon = M.icons.full
30 | end
31 |
32 | local color = beautiful.fg_normal
33 | if bat_now.status == "Charging" then
34 | color = beautiful.bg_success
35 | elseif bat_now.status == "Discharging" then
36 | color = beautiful.bg_warning
37 | elseif bat_now.status == "Full" then
38 | color = beautiful.fg_normal
39 | end
40 |
41 | local list = {
42 | bat_now.perc,
43 | "%",
44 | " ",
45 | }
46 | local text = table.concat(list, " ")
47 |
48 | M.widget.markup = string.format(
49 | " " .. icon .. " %s ",
50 | beautiful.font,
51 | beautiful.font_size,
52 | color,
53 | text
54 | )
55 | end
56 |
57 | M.widget = wibox.widget({
58 | markup = string.format(
59 | " ",
60 | beautiful.font,
61 | beautiful.font_size,
62 | beautiful.fg_normal
63 | ),
64 | align = "center",
65 | valign = "center",
66 | widget = wibox.widget.textbox,
67 | })
68 |
69 | M.setup = function()
70 | helper.newtimer("Battery Status Text", timeout, battery_update, true, true)
71 |
72 | return helper.create_rounded_widget(M.widget, dpi(12))
73 | end
74 |
75 | return M
76 |
--------------------------------------------------------------------------------
/bar/modules/brightness.lua:
--------------------------------------------------------------------------------
1 | local wibox = require("wibox.init")
2 | local beautiful = require("beautiful")
3 | local dpi = beautiful.xresources.apply_dpi
4 | local helper = require("helper")
5 | local awful = require("awful")
6 |
7 | local M = {}
8 |
9 | local step_amount = 5
10 | local brightness_icon = ""
11 |
12 | M.widget = wibox.widget({
13 | markup = brightness_icon,
14 | font = beautiful.icon_font .. " Bold 10",
15 | align = "center",
16 | valign = "center",
17 | widget = wibox.widget.textbox,
18 | })
19 |
20 | local function update_widget(_, stdout)
21 | M.widget.marup = string.format(
22 | "" .. brightness_icon .. " " .. stdout .. "",
23 | beautiful.icon_font,
24 | beautiful.accent
25 | )
26 | end
27 |
28 | awful.widget.watch("sh -c 'brightnessctl -m | cut -d, -f4 | tr -d %'", 5, update_widget, M.widget)
29 |
30 | M.setup = function(opts)
31 | opts = opts or {}
32 | if opts.step ~= nil then
33 | M.step = opts.step
34 | end
35 |
36 | M.widget:buttons(awful.util.table.join(
37 | awful.button({}, 4, function()
38 | awful.spawn("brightnessctl set +" .. step_amount .. "%")
39 | end),
40 | awful.button({}, 5, function()
41 | awful.spawn("brightnessctl set " .. step_amount .. "-%")
42 | end)
43 | ))
44 |
45 | return helper.create_rounded_widget(M.widget, dpi(12))
46 | end
47 |
48 | return M
49 |
--------------------------------------------------------------------------------
/bar/modules/lain_battery.lua:
--------------------------------------------------------------------------------
1 | --[[
2 |
3 | Licensed under GNU General Public License v2
4 | * (c) 2013, Luca CPZ
5 | * (c) 2010-2012, Peter Hofmann
6 |
7 | --]]
8 |
9 | local helpers = require("helper")
10 | local fs = require("gears.filesystem")
11 | local naughty = require("naughty")
12 | local wibox = require("wibox")
13 | local beautiful = require("beautiful")
14 | local math = math
15 | local string = string
16 | local ipairs = ipairs
17 | local tonumber = tonumber
18 |
19 | -- Battery infos
20 | -- lain.widget.bat
21 |
22 | local function factory(args)
23 | local pspath = args.pspath or "/sys/class/power_supply/"
24 |
25 | if not fs.is_dir(pspath) then
26 | naughty.notify({ text = "lain.widget.bat: invalid power supply path", timeout = 0 })
27 | return
28 | end
29 |
30 | args = args or {}
31 |
32 | local bat = { widget = args.widget or wibox.widget.textbox() }
33 | local timeout = args.timeout or 30
34 | local notify = args.notify or "on"
35 | local full_notify = args.full_notify or notify
36 | local n_perc = args.n_perc or { 5, 15 }
37 | local batteries = args.batteries or (args.battery and { args.battery }) or {}
38 | local ac = args.ac or "AC0"
39 | local settings = args.settings or function() end
40 |
41 | function bat.get_batteries()
42 | helpers.line_callback("ls -1 " .. pspath, function(line)
43 | local bstr = string.match(line, "BAT%w+")
44 | if bstr then
45 | batteries[#batteries + 1] = bstr
46 | else
47 | ac = string.match(line, "A%w+") or ac
48 | end
49 | end)
50 | end
51 |
52 | if #batteries == 0 then
53 | bat.get_batteries()
54 | end
55 |
56 | bat_notification_critical_preset = {
57 | title = "Battery exhausted",
58 | text = "Shutdown imminent",
59 | timeout = 15,
60 | fg = beautiful.fg_urgent,
61 | bg = beautiful.bg_urgent,
62 | }
63 |
64 | bat_notification_low_preset = {
65 | title = "Battery low",
66 | text = "Plug the cable!",
67 | timeout = 15,
68 | fg = beautiful.fg_warning,
69 | bg = beautiful.bg_warning,
70 | }
71 |
72 | bat_notification_charged_preset = {
73 | title = "Battery full",
74 | text = "You can unplug the cable",
75 | timeout = 15,
76 | fg = beautiful.fg_success,
77 | bg = beautiful.bg_success,
78 | }
79 |
80 | bat_now = {
81 | status = "N/A",
82 | ac_status = "N/A",
83 | perc = "N/A",
84 | time = "N/A",
85 | watt = "N/A",
86 | capacity = "N/A",
87 | }
88 |
89 | bat_now.n_status = {}
90 | bat_now.n_perc = {}
91 | bat_now.n_capacity = {}
92 | for i = 1, #batteries do
93 | bat_now.n_status[i] = "N/A"
94 | bat_now.n_perc[i] = 0
95 | bat_now.n_capacity[i] = 0
96 | end
97 |
98 | -- used to notify full charge only once before discharging
99 | local fullnotification = false
100 |
101 | function bat.update()
102 | -- luacheck: globals bat_now
103 | local sum_rate_current = 0
104 | local sum_rate_voltage = 0
105 | local sum_rate_power = 0
106 | local sum_rate_energy = 0
107 | local sum_energy_now = 0
108 | local sum_energy_full = 0
109 | local sum_charge_full = 0
110 | local sum_charge_design = 0
111 |
112 | for i, battery in ipairs(batteries) do
113 | local bstr = pspath .. battery
114 | local present = helpers.first_line(bstr .. "/present")
115 |
116 | if tonumber(present) == 1 then
117 | -- current_now(I)[uA], voltage_now(U)[uV], power_now(P)[uW]
118 | local rate_current = tonumber(helpers.first_line(bstr .. "/current_now"))
119 | local rate_voltage = tonumber(helpers.first_line(bstr .. "/voltage_now"))
120 | local rate_power = tonumber(helpers.first_line(bstr .. "/power_now"))
121 | local charge_full = tonumber(helpers.first_line(bstr .. "/charge_full"))
122 | local charge_design = tonumber(helpers.first_line(bstr .. "/charge_full_design"))
123 |
124 | -- energy_now(P)[uWh], charge_now(I)[uAh]
125 | local energy_now =
126 | tonumber(helpers.first_line(bstr .. "/energy_now") or helpers.first_line(bstr .. "/charge_now"))
127 |
128 | -- energy_full(P)[uWh], charge_full(I)[uAh]
129 | local energy_full = tonumber(helpers.first_line(bstr .. "/energy_full") or charge_full)
130 |
131 | local energy_percentage = tonumber(helpers.first_line(bstr .. "/capacity"))
132 | or math.floor((energy_now / energy_full) * 100)
133 |
134 | bat_now.n_status[i] = helpers.first_line(bstr .. "/status") or "N/A"
135 | bat_now.n_perc[i] = energy_percentage or bat_now.n_perc[i]
136 |
137 | if not charge_design or charge_design == 0 then
138 | bat_now.n_capacity[i] = 0
139 | else
140 | bat_now.n_capacity[i] = math.floor((charge_full / charge_design) * 100)
141 | end
142 |
143 | sum_rate_current = sum_rate_current + (rate_current or 0)
144 | sum_rate_voltage = sum_rate_voltage + (rate_voltage or 0)
145 | sum_rate_power = sum_rate_power + (rate_power or 0)
146 | sum_rate_energy = sum_rate_energy + (rate_power or (((rate_voltage or 0) * (rate_current or 0)) / 1e6))
147 | sum_energy_now = sum_energy_now + (energy_now or 0)
148 | sum_energy_full = sum_energy_full + (energy_full or 0)
149 | sum_charge_full = sum_charge_full + (charge_full or 0)
150 | sum_charge_design = sum_charge_design + (charge_design or 0)
151 | end
152 | end
153 |
154 | bat_now.capacity = math.floor(math.min(100, (sum_charge_full / sum_charge_design) * 100))
155 |
156 | -- When one of the battery is charging, others' status are either
157 | -- "Full", "Unknown" or "Charging". When the laptop is not plugged in,
158 | -- one or more of the batteries may be full, but only one battery
159 | -- discharging suffices to set global status to "Discharging".
160 | bat_now.status = bat_now.n_status[1] or "N/A"
161 | for _, status in ipairs(bat_now.n_status) do
162 | if status == "Discharging" or status == "Charging" then
163 | bat_now.status = status
164 | end
165 | end
166 | bat_now.ac_status = tonumber(helpers.first_line(string.format("%s%s/online", pspath, ac))) or "N/A"
167 |
168 | if bat_now.status ~= "N/A" then
169 | if bat_now.status ~= "Full" and sum_rate_power == 0 and bat_now.ac_status == 1 then
170 | bat_now.perc = math.floor(math.min(100, (sum_energy_now / sum_energy_full) * 100))
171 | bat_now.time = "00:00"
172 | bat_now.watt = 0
173 |
174 | -- update {perc,time,watt} iff battery not full and rate > 0
175 | elseif bat_now.status ~= "Full" then
176 | local rate_time = 0
177 | -- Calculate time and watt if rates are greater then 0
178 | if sum_rate_power > 0 or sum_rate_current > 0 then
179 | local div = (sum_rate_power > 0 and sum_rate_power) or sum_rate_current
180 |
181 | if bat_now.status == "Charging" then
182 | rate_time = (sum_energy_full - sum_energy_now) / div
183 | else -- Discharging
184 | rate_time = sum_energy_now / div
185 | end
186 |
187 | if 0 < rate_time and rate_time < 0.01 then -- check for magnitude discrepancies (#199)
188 | rate_time_magnitude = math.abs(math.floor(math.log10(rate_time)))
189 | rate_time = rate_time * 10 ^ (rate_time_magnitude - 2)
190 | end
191 | end
192 |
193 | local hours = math.floor(rate_time)
194 | local minutes = math.floor((rate_time - hours) * 60)
195 | bat_now.perc = math.floor(math.min(100, (sum_energy_now / sum_energy_full) * 100))
196 | bat_now.time = string.format("%02d:%02d", hours, minutes)
197 | bat_now.watt = tonumber(string.format("%.2f", sum_rate_energy / 1e6))
198 | elseif bat_now.status == "Full" then
199 | bat_now.perc = 100
200 | bat_now.time = "00:00"
201 | bat_now.watt = 0
202 | end
203 | end
204 |
205 | widget = bat.widget
206 | settings()
207 |
208 | -- notifications for critical, low, and full levels
209 | if notify == "on" then
210 | if bat_now.status == "Discharging" then
211 | if tonumber(bat_now.perc) <= n_perc[1] then
212 | bat.id = naughty.notify({
213 | preset = bat_notification_critical_preset,
214 | replaces_id = bat.id,
215 | }).id
216 | elseif tonumber(bat_now.perc) <= n_perc[2] then
217 | bat.id = naughty.notify({
218 | preset = bat_notification_low_preset,
219 | replaces_id = bat.id,
220 | }).id
221 | end
222 | fullnotification = false
223 | elseif bat_now.status == "Full" and full_notify == "on" and not fullnotification then
224 | bat.id = naughty.notify({
225 | preset = bat_notification_charged_preset,
226 | replaces_id = bat.id,
227 | }).id
228 | fullnotification = true
229 | end
230 | end
231 | end
232 |
233 | helpers.newtimer("batteries", timeout, bat.update)
234 |
235 | return bat
236 | end
237 |
238 | return factory
239 |
--------------------------------------------------------------------------------
/bar/modules/lain_mpd.lua:
--------------------------------------------------------------------------------
1 | --[[
2 |
3 | Licensed under GNU General Public License v2
4 | * (c) 2013, Luca CPZ
5 | * (c) 2010, Adrian C.
6 |
7 | --]]
8 |
9 | local helpers = require("helper")
10 | local shell = require("awful.util").shell
11 | local escape_f = require("awful.util").escape
12 | local focused = require("awful.screen").focused
13 | local naughty = require("naughty")
14 | local wibox = require("wibox")
15 | local os = os
16 | local string = string
17 |
18 | -- MPD infos
19 | -- lain.widget.mpd
20 |
21 | local function factory(args)
22 | args = args or {}
23 |
24 | local mpd = { widget = args.widget or wibox.widget.textbox() }
25 | local timeout = args.timeout or 2
26 | local password = (args.password and #args.password > 0 and string.format("password %s\\n", args.password)) or ""
27 | local host = args.host or os.getenv("MPD_HOST") or "127.0.0.1"
28 | local port = args.port or os.getenv("MPD_PORT") or "6600"
29 | local music_dir = args.music_dir or os.getenv("HOME") .. "/Music"
30 | local cover_pattern = args.cover_pattern or "*\\.(jpg|jpeg|png|gif)$"
31 | local cover_size = args.cover_size or 100
32 | local default_art = args.default_art
33 | local notify = args.notify or "on"
34 | local followtag = args.followtag or false
35 | local settings = args.settings or function() end
36 |
37 | local mpdh = string.format("telnet://%s:%s", host, port)
38 | local echo = string.format('printf "%sstatus\\ncurrentsong\\nclose\\n"', password)
39 | local cmd = string.format("%s | curl --connect-timeout 1 -fsm 3 %s", echo, mpdh)
40 |
41 | mpd_notification_preset = { title = "Now playing", timeout = 6 }
42 |
43 | helpers.set_map("current mpd track", nil)
44 |
45 | function mpd.update()
46 | helpers.async({ shell, "-c", cmd }, function(f)
47 | mpd_now = {
48 | random_mode = false,
49 | single_mode = false,
50 | repeat_mode = false,
51 | consume_mode = false,
52 | pls_pos = "N/A",
53 | pls_len = "N/A",
54 | state = "N/A",
55 | file = "N/A",
56 | name = "N/A",
57 | artist = "N/A",
58 | title = "N/A",
59 | album = "N/A",
60 | genre = "N/A",
61 | track = "N/A",
62 | date = "N/A",
63 | time = "N/A",
64 | elapsed = "N/A",
65 | volume = "N/A",
66 | }
67 |
68 | for line in string.gmatch(f, "[^\n]+") do
69 | for k, v in string.gmatch(line, "([%w]+):[%s](.*)$") do
70 | if k == "state" then
71 | mpd_now.state = v
72 | elseif k == "file" then
73 | mpd_now.file = v
74 | elseif k == "Name" then
75 | mpd_now.name = escape_f(v)
76 | elseif k == "Artist" then
77 | mpd_now.artist = escape_f(v)
78 | elseif k == "Title" then
79 | mpd_now.title = escape_f(v)
80 | elseif k == "Album" then
81 | mpd_now.album = escape_f(v)
82 | elseif k == "Genre" then
83 | mpd_now.genre = escape_f(v)
84 | elseif k == "Track" then
85 | mpd_now.track = escape_f(v)
86 | elseif k == "Date" then
87 | mpd_now.date = escape_f(v)
88 | elseif k == "Time" then
89 | mpd_now.time = v
90 | elseif k == "elapsed" then
91 | mpd_now.elapsed = string.match(v, "%d+")
92 | elseif k == "song" then
93 | mpd_now.pls_pos = v
94 | elseif k == "playlistlength" then
95 | mpd_now.pls_len = v
96 | elseif k == "repeat" then
97 | mpd_now.repeat_mode = v ~= "0"
98 | elseif k == "single" then
99 | mpd_now.single_mode = v ~= "0"
100 | elseif k == "random" then
101 | mpd_now.random_mode = v ~= "0"
102 | elseif k == "consume" then
103 | mpd_now.consume_mode = v ~= "0"
104 | elseif k == "volume" then
105 | mpd_now.volume = v
106 | end
107 | end
108 | end
109 |
110 | mpd_notification_preset.text =
111 | string.format("%s (%s) - %s\n%s", mpd_now.artist, mpd_now.album, mpd_now.date, mpd_now.title)
112 | widget = mpd.widget
113 | settings()
114 |
115 | if mpd_now.state == "play" then
116 | if notify == "on" and mpd_now.title ~= helpers.get_map("current mpd track") then
117 | helpers.set_map("current mpd track", mpd_now.title)
118 |
119 | if followtag then
120 | mpd_notification_preset.screen = focused()
121 | end
122 |
123 | local common = {
124 | preset = mpd_notification_preset,
125 | icon = default_art,
126 | icon_size = cover_size,
127 | replaces_id = mpd.id,
128 | }
129 |
130 | if not string.match(mpd_now.file, "http.*://") then -- local file instead of http stream
131 | local path = string.format("%s/%s", music_dir, string.match(mpd_now.file, ".*/"))
132 | local cover = string.format(
133 | "find '%s' -maxdepth 1 -type f | egrep -i -m1 '%s'",
134 | path:gsub("'", "'\\''"),
135 | cover_pattern
136 | )
137 | helpers.async({ shell, "-c", cover }, function(current_icon)
138 | common.icon = current_icon:gsub("\n", "")
139 | if #common.icon == 0 then
140 | common.icon = nil
141 | end
142 | mpd.id = naughty.notify(common).id
143 | end)
144 | else
145 | mpd.id = naughty.notify(common).id
146 | end
147 | end
148 | elseif mpd_now.state ~= "pause" then
149 | helpers.set_map("current mpd track", nil)
150 | end
151 | end)
152 | end
153 |
154 | mpd.timer = helpers.newtimer("mpd", timeout, mpd.update, true, true)
155 |
156 | return mpd
157 | end
158 |
159 | return factory
160 |
--------------------------------------------------------------------------------
/bar/modules/mpd.lua:
--------------------------------------------------------------------------------
1 | local wibox = require("wibox.init")
2 | local beautiful = require("beautiful")
3 | local helper = require("helper")
4 | local awful = require("awful")
5 |
6 | local M = {}
7 |
8 | local timeout = 2
9 |
10 | M.icons = {
11 | prev = "",
12 | play = "",
13 | pause = "",
14 | next = "",
15 | stop = "",
16 | }
17 |
18 | local function mpd_update()
19 | local list = {
20 | M.icons[mpd_now.state],
21 | mpd_now.title,
22 | }
23 | local text = table.concat(list, " ")
24 | M.widget.markup = string.format(
25 | " %s ",
26 | beautiful.font,
27 | beautiful.font_size,
28 | beautiful.fg_normal,
29 | text
30 | )
31 | end
32 |
33 | M.widget = wibox.widget({
34 | markup = string.format(
35 | " Now Playing ",
36 | beautiful.font,
37 | beautiful.font_size,
38 | beautiful.fg_normal
39 | ),
40 | align = "center",
41 | valign = "center",
42 | widget = wibox.widget.textbox,
43 | })
44 |
45 | M.setup = function()
46 | helper.newtimer("Mpd Status Text", timeout, mpd_update, true, true)
47 |
48 | M.widget:connect_signal("button::press", function(_, _, _, button)
49 | if button == 1 then
50 | awful.spawn("mpc toggle")
51 | end
52 | if button == 3 then
53 | awful.spawn("ghostty -e ncmpcpp")
54 | end
55 | if button == 4 then
56 | awful.spawn("mpc volume +5")
57 | end
58 | if button == 5 then
59 | awful.spawn("mpc volume -5")
60 | end
61 | end)
62 |
63 | return M.widget
64 | end
65 |
66 | return M
67 |
--------------------------------------------------------------------------------
/bar/modules/systray.lua:
--------------------------------------------------------------------------------
1 | local wibox = require("wibox.init")
2 | local helper = require("helper")
3 | local beautiful = require("beautiful")
4 | local awful = require("awful")
5 | local dpi = beautiful.xresources.apply_dpi
6 |
7 | local M = {}
8 |
9 | M.setup = function()
10 | local tray = wibox.widget.systray()
11 | tray.opacity = 1.0
12 |
13 | local widget = wibox.widget({
14 | {
15 | widget = tray,
16 | },
17 | left = dpi(6),
18 | top = dpi(2),
19 | bottom = dpi(2),
20 | right = dpi(6),
21 | widget = wibox.container.margin,
22 | })
23 |
24 | return helper.create_rounded_widget(widget, dpi(10))
25 | end
26 |
27 | return M
28 |
--------------------------------------------------------------------------------
/bar/modules/taglist.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local fancy_taglist = require("external.fancy_taglist")
3 | local beautiful = require("beautiful")
4 | local dpi = beautiful.xresources.apply_dpi
5 | local helper = require("helper")
6 |
7 | local M = {}
8 |
9 | local tag_buttons = {
10 | awful.button({}, 1, function(t)
11 | t:view_only()
12 | end),
13 | awful.button({}, 4, function(t)
14 | awful.tag.viewprev(t.screen)
15 | end),
16 | awful.button({}, 5, function(t)
17 | awful.tag.viewnext(t.screen)
18 | end),
19 | }
20 |
21 | local tasklist_buttons = {
22 | awful.button({}, 1, function(c)
23 | c:emit_signal("request::activate", "tasklist", { raise = true, switch_to_tags = true })
24 | end),
25 | awful.button({}, 3, function()
26 | awful.menu.client_list({ theme = { width = 250 } })
27 | end),
28 | awful.button({}, 4, function()
29 | awful.client.focus.byidx(1)
30 | end),
31 | awful.button({}, 5, function()
32 | awful.client.focus.byidx(-1)
33 | end),
34 | }
35 |
36 | M.setup = function(s)
37 | s.mytaglist = fancy_taglist.new({
38 | screen = s,
39 | taglist = { buttons = tag_buttons },
40 | tasklist = { buttons = tasklist_buttons },
41 | })
42 |
43 | local rounded_widget = helper.create_rounded_widget(s.mytaglist, dpi(12))
44 |
45 | return rounded_widget
46 | end
47 |
48 | return M
49 |
--------------------------------------------------------------------------------
/bar/modules/time.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox.init")
3 | local beautiful = require("beautiful")
4 |
5 | local M = {}
6 |
7 | M.setup = function()
8 | local widget = wibox.widget.textclock(
9 | string.format(
10 | "%%a %%b %%d, %%I:%%M",
11 | beautiful.font,
12 | beautiful.font_size,
13 | beautiful.fg_normal
14 | )
15 | )
16 | return widget
17 | end
18 |
19 | return M
20 |
--------------------------------------------------------------------------------
/bar/modules/volume.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox.init")
3 | local gears = require("gears")
4 | local beautiful = require("beautiful")
5 | local helper = require("helper")
6 | local dpi = beautiful.xresources.apply_dpi
7 | local naughty = require("naughty")
8 |
9 | local M = {}
10 |
11 | M.widget = wibox.widget({
12 | {
13 | id = "volume_icon",
14 | markup = string.format("", beautiful.accent),
15 | widget = wibox.widget.textbox,
16 | },
17 | {
18 | id = "volume_slider",
19 | forced_height = dpi(1),
20 | forced_width = dpi(59),
21 | color = beautiful.accent,
22 | background_color = beautiful.module_bg,
23 | margins = dpi(2),
24 | paddings = dpi(2),
25 | max_value = 150,
26 | ticks = true,
27 | ticks_size = dpi(6),
28 | shape = gears.shape.rounded_bar,
29 | widget = wibox.widget.progressbar,
30 | },
31 | {
32 | id = "volume_text",
33 | markup = string.format(
34 | " 0%% ",
35 | beautiful.font,
36 | beautiful.font_size,
37 | beautiful.fg_normal
38 | ),
39 | align = "center",
40 | valign = "center",
41 | widget = wibox.widget.textbox,
42 | },
43 | forced_width = dpi(148),
44 | layout = wibox.layout.align.horizontal,
45 | })
46 |
47 | local icons = {
48 | muted = " ",
49 | low = " ",
50 | med = " ",
51 | high = " ",
52 | }
53 |
54 | local critical_bar_color = {
55 | type = "linear",
56 | from = { 0, 0 },
57 | to = { 96, 0 },
58 | stops = {
59 | { 0, beautiful.accent },
60 | { 1, beautiful.bg_urgent },
61 | },
62 | }
63 |
64 | local debounce_timer = nil
65 |
66 | local function set_volume_debounced(volume)
67 | if debounce_timer then
68 | debounce_timer:stop()
69 | end
70 |
71 | debounce_timer = gears.timer.start_new(0.2, function()
72 | awful.spawn("wpctl set-volume @DEFAULT_SINK@ " .. volume)
73 | end)
74 | end
75 |
76 | local function update_volume_sys_event(icon, volume)
77 | M.widget.volume_icon:set_markup(string.format("" .. icon .. "", beautiful.accent))
78 | M.widget.volume_text.markup = string.format(
79 | " " .. volume .. "%% ",
80 | beautiful.font,
81 | beautiful.font_size,
82 | beautiful.fg_normal
83 | )
84 | M.widget.volume_slider:set_value(volume)
85 | if volume > 100 then
86 | M.widget.volume_slider.color = critical_bar_color
87 | else
88 | M.widget.volume_slider.color = beautiful.accent
89 | end
90 | end
91 |
92 | local function sync_volume_changes()
93 | awful.spawn.easy_async("wpctl get-volume @DEFAULT_SINK@", function(stdout)
94 | local icon
95 | local volume = tonumber(stdout:match("(%d%.%d%d?)")) * 100
96 | if stdout:match("MUTED") then
97 | icon = icons.muted
98 | update_volume_sys_event(icon, volume)
99 | return
100 | end
101 | if volume < 50 then
102 | icon = icons.low
103 | elseif volume < 90 then
104 | icon = icons.med
105 | else
106 | icon = icons.high
107 | end
108 |
109 | update_volume_sys_event(icon, volume)
110 | end)
111 | end
112 |
113 | M.setup = function()
114 | -- kill process if pactl subscribe is already running
115 | awful.spawn.easy_async_with_shell(
116 | "ps x | grep 'pactl subscribe' | grep -v grep | awk '{print $1}' | xargs kill",
117 | function()
118 | -- start new one with callback
119 | awful.spawn.with_line_callback(
120 | [[bash -c "pactl subscribe | grep --line-buffered \"Event 'change' on sink #\""]],
121 | {
122 | stdout = function(_)
123 | sync_volume_changes()
124 | end,
125 | }
126 | )
127 | end
128 | )
129 |
130 | M.widget.volume_slider:connect_signal("property::value", function()
131 | set_volume_debounced(M.widget.volume_slider.value / 100)
132 | end)
133 |
134 | M.widget.volume_slider:connect_signal("button::press", function(_, _, _, button)
135 | if button == 4 then
136 | awful.spawn("wpctl set-volume @DEFAULT_SINK@ 5%+")
137 | end
138 | if button == 5 then
139 | awful.spawn("wpctl set-volume @DEFAULT_SINK@ 5%-")
140 | end
141 | end)
142 |
143 | M.widget.volume_icon:connect_signal("button::press", function(_, _, _, button)
144 | if button == 1 then
145 | awful.spawn("wpctl set-mute @DEFAULT_SINK@ toggle")
146 | end
147 | if button == 3 then
148 | awful.spawn("pavucontrol-qt")
149 | end
150 | end)
151 |
152 | helper.hover_hand(M.widget.volume_icon)
153 | helper.hover_hand(M.widget.volume_slider)
154 |
155 | sync_volume_changes()
156 |
157 | return helper.create_rounded_widget(M.widget, dpi(12))
158 | end
159 |
160 | return M
161 |
--------------------------------------------------------------------------------
/external/fancy_taglist.lua:
--------------------------------------------------------------------------------
1 | -- awesomewm fancy_taglist: a taglist that contains a tasklist for each tag.
2 | -- Usage (add s.mytaglist to the wibar):
3 | -- awful.screen.connect_for_each_screen(function(s)
4 | -- ...
5 | -- local fancy_taglist = require("fancy_taglist")
6 | -- s.mytaglist = fancy_taglist.new({
7 | -- screen = s,
8 | -- taglist = { buttons = mytagbuttons },
9 | -- tasklist = { buttons = mytasklistbuttons }
10 | -- })
11 | -- ...
12 | -- end)
13 | --
14 | -- If you want rounded corners, try this in your theme:
15 | -- theme.taglist_shape = function(cr, w, h)
16 | -- return gears.shape.rounded_rect(cr, w, h, theme.border_radius)
17 | -- end
18 | local awful = require("awful")
19 | local beautiful = require("beautiful")
20 | local gears = require("gears")
21 | local wibox = require("wibox")
22 |
23 | local dpi = beautiful.xresources.apply_dpi
24 | local internal_spacing = dpi(4)
25 | local box_height = dpi(4)
26 | local box_width = dpi(8)
27 | local icon_size = dpi(16)
28 |
29 | local function box_margins(widget)
30 | return {
31 | { widget, widget = wibox.container.place },
32 | top = box_height,
33 | bottom = box_height,
34 | left = box_width,
35 | right = box_width,
36 | widget = wibox.container.margin,
37 | }
38 | end
39 |
40 | local function constrain_icon(widget)
41 | return {
42 | {
43 | widget,
44 | height = icon_size,
45 | strategy = "exact",
46 | widget = wibox.container.constraint,
47 | },
48 | widget = wibox.container.place,
49 | }
50 | end
51 |
52 | local function fancy_tasklist(cfg, tag)
53 | local function only_this_tag(c, _)
54 | for _, t in ipairs(c:tags()) do
55 | if t == tag then
56 | return true
57 | end
58 | end
59 | return false
60 | end
61 |
62 | local overrides = {
63 | filter = only_this_tag,
64 | layout = {
65 | spacing = beautiful.taglist_spacing,
66 | layout = wibox.layout.fixed.horizontal,
67 | },
68 | widget_template = {
69 | id = "clienticon",
70 | widget = awful.widget.clienticon,
71 | create_callback = function(self, c, _, _)
72 | self:get_children_by_id("clienticon")[1].client = c
73 | end,
74 | },
75 | }
76 | return awful.widget.tasklist(gears.table.join(cfg, overrides))
77 | end
78 |
79 | local module = {}
80 |
81 | -- @param cfg.screen
82 | -- @param cfg.tasklist -> see awful.widget.tasklist
83 | -- @param cfg.taglist -> see awful.widget.taglist
84 | function module.new(cfg)
85 | cfg = cfg or {}
86 | local taglist_cfg = cfg.taglist or {}
87 | local tasklist_cfg = cfg.tasklist or {}
88 |
89 | local screen = cfg.screen or awful.screen.focused()
90 | taglist_cfg.screen = screen
91 | tasklist_cfg.screen = screen
92 |
93 | local function update_callback(self, tag, _, _)
94 | -- make sure that empty tasklists take up no extra space
95 | local list_separator = self:get_children_by_id("list_separator")[1]
96 | if #tag:clients() == 0 then
97 | list_separator.spacing = 0
98 | else
99 | list_separator.spacing = internal_spacing
100 | end
101 | end
102 |
103 | local function create_callback(self, tag, _index, _tags)
104 | local tasklist = fancy_tasklist(tasklist_cfg, tag)
105 | self:get_children_by_id("tasklist_placeholder")[1]:add(tasklist)
106 | update_callback(self, tag, _index, _tags)
107 | end
108 |
109 | local overrides = {
110 | filter = awful.widget.taglist.filter.all,
111 | widget_template = {
112 | box_margins({
113 | -- tag
114 | {
115 | id = "text_role",
116 | widget = wibox.widget.textbox,
117 | align = "center",
118 | },
119 | -- tasklist
120 | constrain_icon({
121 | id = "tasklist_placeholder",
122 | layout = wibox.layout.fixed.horizontal,
123 | }),
124 | id = "list_separator",
125 | spacing = internal_spacing,
126 | layout = wibox.layout.fixed.horizontal,
127 | }),
128 | id = "background_role",
129 | widget = wibox.container.background,
130 | create_callback = create_callback,
131 | update_callback = update_callback,
132 | },
133 | }
134 | return awful.widget.taglist(gears.table.join(taglist_cfg, overrides))
135 | end
136 |
137 | return module
138 |
--------------------------------------------------------------------------------
/helper.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local wibox = require("wibox")
3 | local gears = require("gears")
4 | local beautiful = require("beautiful")
5 | local dpi = beautiful.xresources.apply_dpi
6 |
7 | local M = {}
8 | local helpers = {}
9 | helpers.map_table = {}
10 |
11 | --- adds hover properties to a background container
12 | --- to add hover to box_widget use 'widget:get_children_by_id("box_container")\[1\]' as widget name
13 | ---@param t {widget: table, newbg: string, oldbg: string, hover_cursor: string}
14 | M.hover = function(t)
15 | setmetatable(t, { __index = { hover_cursor = "hand1" } })
16 | local widget = t.widget
17 | local newbg = t.newbg
18 | local oldbg = t.oldbg
19 | local hover_cursor = t.hover_cursor
20 | widget:connect_signal("mouse::enter", function()
21 | widget:set_bg(newbg)
22 | ---@diagnostic disable-next-line: undefined-global
23 | local w = mouse.current_wibox
24 | if w then
25 | w.cursor = hover_cursor
26 | end
27 | end)
28 | widget:connect_signal("mouse::leave", function()
29 | widget:set_bg(oldbg)
30 | ---@diagnostic disable-next-line: undefined-global
31 | local w = mouse.current_wibox
32 | if w then
33 | w.cursor = "left_ptr"
34 | end
35 | end)
36 | end
37 |
38 | --- same as hover but dosent change the background
39 | ---@see M.hover
40 | ---@param widget table
41 | M.hover_hand = function(widget)
42 | widget:connect_signal("mouse::enter", function()
43 | ---@diagnostic disable-next-line: undefined-global
44 | local w = mouse.current_wibox
45 | if w then
46 | w.cursor = "hand1"
47 | end
48 | end)
49 | widget:connect_signal("mouse::leave", function()
50 | ---@diagnostic disable-next-line: undefined-global
51 | local w = mouse.current_wibox
52 | if w then
53 | w.cursor = "left_ptr"
54 | end
55 | end)
56 | end
57 |
58 | M.padding_horizontal = function(dpi)
59 | return wibox.widget({
60 | forced_width = dpi,
61 | widget = wibox.container.background,
62 | })
63 | end
64 |
65 | M.create_rounded_widget = function(widget, amount)
66 | local container = wibox.widget({
67 | {
68 | {
69 | M.padding_horizontal(dpi(6)),
70 | widget,
71 | M.padding_horizontal(dpi(6)),
72 | layout = wibox.layout.fixed.horizontal,
73 | },
74 | id = "container",
75 | bg = beautiful.module_bg,
76 | shape = function(cr, width, height)
77 | gears.shape.rounded_rect(cr, width, height, amount)
78 | end,
79 | widget = wibox.container.background,
80 | },
81 | left = dpi(8),
82 | right = dpi(8),
83 | top = dpi(4),
84 | bottom = dpi(4),
85 | widget = wibox.container.margin,
86 | })
87 |
88 | -- lrtb
89 | return container
90 | end
91 |
92 | M.set_map = function(element, value)
93 | helpers.map_table[element] = value
94 | end
95 |
96 | M.get_map = function(element)
97 | return helpers.map_table[element]
98 | end
99 |
100 | M.async = function(cmd, callback)
101 | return awful.spawn.easy_async(cmd, function(stdout, _, _, exit_code)
102 | callback(stdout, exit_code)
103 | end)
104 | end
105 |
106 | M.newtimer = function(name, timeout, fun, nostart, stoppable)
107 | if not name or #name == 0 then
108 | return
109 | end
110 | local key = stoppable and name or timeout
111 | helpers.timer_table = helpers.timer_table or {}
112 | if not helpers.timer_table[key] then
113 | helpers.timer_table[key] = gears.timer({ timeout = timeout })
114 | helpers.timer_table[key]:start()
115 | end
116 | helpers.timer_table[key]:connect_signal("timeout", fun)
117 | if not nostart then
118 | helpers.timer_table[key]:emit_signal("timeout")
119 | end
120 | return stoppable and helpers.timer_table[key]
121 | end
122 |
123 | M.first_line = function(path)
124 | local file, first = io.open(path, "rb"), nil
125 | if file then
126 | first = file:read("*l")
127 | file:close()
128 | end
129 | return first
130 | end
131 |
132 | M.line_callback = function(cmd, callback)
133 | return awful.spawn.with_line_callback(cmd, {
134 | stdout = function(line)
135 | callback(line)
136 | end,
137 | })
138 | end
139 |
140 | return M
141 |
--------------------------------------------------------------------------------
/keys.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful.init")
2 | local hotkeys_popup = require("awful.hotkeys_popup")
3 |
4 | local modkey = "Mod4"
5 | local terminal = "ghostty"
6 |
7 | -- General Awesome keys
8 | awful.keyboard.append_global_keybindings({
9 | awful.key({ modkey }, "F1", hotkeys_popup.show_help, { description = "show help", group = "awesome" }),
10 |
11 | awful.key({ modkey, "Shift" }, "r", awesome.restart, { description = "reload awesome", group = "awesome" }),
12 |
13 | awful.key({ modkey }, "Return", function()
14 | awful.spawn(terminal)
15 | end, { description = "open a terminal", group = "launcher" }),
16 |
17 | awful.key({}, "Print", function()
18 | awful.spawn.with_shell("scrot -s -e 'mv $f ~/Pictures/'")
19 | end, { description = "Print Screen with scrot" }),
20 |
21 | awful.key({ "Mod1" }, "space", function()
22 | awful.spawn("rofi -show drun")
23 | end, { description = "Rofi", group = "launcher" }),
24 | })
25 |
26 | -- Tags related keybindings
27 | awful.keyboard.append_global_keybindings({
28 | awful.key({ modkey }, "Tab", function()
29 | local c = awful.client.focus.history.list[2]
30 | client.focus = c
31 | local t = client.focus and client.focus.first_tag or nil
32 | if t then
33 | t:view_only()
34 | end
35 | c:raise()
36 | end, { description = "Toggle Between clients", group = "Tag" }),
37 | })
38 |
39 | -- Focus related keybindings
40 | awful.keyboard.append_global_keybindings({
41 | awful.key({ modkey }, "j", function()
42 | awful.client.focus.bydirection("down", client.focused)
43 | end, { description = "focus by direction down", group = "client" }),
44 |
45 | awful.key({ modkey }, "k", function()
46 | awful.client.focus.bydirection("up", client.focused)
47 | end, { description = "focus by direction up", group = "client" }),
48 |
49 | awful.key({ modkey }, "h", function()
50 | awful.client.focus.bydirection("left", client.focused)
51 | end, { description = "focus by direction left", group = "client" }),
52 |
53 | awful.key({ modkey }, "l", function()
54 | awful.client.focus.bydirection("right", client.focused)
55 | end, { description = "focus by direction down", group = "client" }),
56 | })
57 |
58 | -- Layout related keybindings
59 | awful.keyboard.append_global_keybindings({
60 | awful.key({ modkey, "Shift" }, "j", function()
61 | awful.client.swap.bydirection("down", client.focused)
62 | end, { description = "swap by direction down", group = "client" }),
63 |
64 | awful.key({ modkey, "Shift" }, "k", function()
65 | awful.client.swap.bydirection("up", client.focused)
66 | end, { description = "swap by direction up", group = "client" }),
67 |
68 | awful.key({ modkey, "Shift" }, "h", function()
69 | awful.client.swap.bydirection("left", client.focused)
70 | end, { description = "swap by direction left", group = "client" }),
71 |
72 | awful.key({ modkey, "Shift" }, "l", function()
73 | awful.client.swap.bydirection("right", client.focused)
74 | end, { description = "swap by direction right", group = "client" }),
75 |
76 | awful.key({ modkey }, "u", awful.client.urgent.jumpto, { description = "jump to urgent client", group = "client" }),
77 |
78 | awful.key({ modkey }, "space", function()
79 | awful.layout.inc(1)
80 | end, { description = "select next", group = "layout" }),
81 |
82 | awful.key({ modkey, "Shift" }, "space", function()
83 | awful.layout.inc(-1)
84 | end, { description = "select previous", group = "layout" }),
85 |
86 | awful.key({ modkey, "Control" }, "k", function()
87 | awful.client.incwfact(-0.05)
88 | end, { description = "Resize by direction up", group = "client" }),
89 |
90 | awful.key({ modkey, "Control" }, "j", function()
91 | awful.client.incwfact(0.05)
92 | end, { description = "Resize by direction down", group = "client" }),
93 |
94 | awful.key({ modkey, "Control" }, "h", function()
95 | awful.tag.incmwfact(-0.05)
96 | end, { description = "Resize by direction left", group = "client" }),
97 |
98 | awful.key({ modkey, "Control" }, "l", function()
99 | awful.tag.incmwfact(0.05)
100 | end, { description = "Resize by direction right", group = "client" }),
101 | })
102 |
103 | awful.keyboard.append_global_keybindings({
104 | awful.key({
105 | modifiers = { modkey },
106 | keygroup = "numrow",
107 | description = "only view tag",
108 | group = "tag",
109 | on_press = function(index)
110 | local screen = awful.screen.focused()
111 | local tag = screen.tags[index]
112 | if tag then
113 | tag:view_only()
114 | end
115 | end,
116 | }),
117 | awful.key({
118 | modifiers = { modkey, "Control" },
119 | keygroup = "numrow",
120 | description = "toggle tag",
121 | group = "tag",
122 | on_press = function(index)
123 | local screen = awful.screen.focused()
124 | local tag = screen.tags[index]
125 | if tag then
126 | awful.tag.viewtoggle(tag)
127 | end
128 | end,
129 | }),
130 | awful.key({
131 | modifiers = { modkey, "Shift" },
132 | keygroup = "numrow",
133 | description = "move focused client to tag",
134 | group = "tag",
135 | on_press = function(index)
136 | if client.focus then
137 | local tag = client.focus.screen.tags[index]
138 | if tag then
139 | client.focus:move_to_tag(tag)
140 | end
141 | end
142 | end,
143 | }),
144 | })
145 |
146 | -- Some Function Keys
147 | awful.keyboard.append_global_keybindings({
148 | awful.key({}, "XF86MonBrightnessUp", function()
149 | awful.spawn("brightnessctl set +10", false)
150 | end, { description = "increase brightness by 10%", group = "hotkeys" }),
151 | awful.key({}, "XF86MonBrightnessDown", function()
152 | awful.spawn("brightnessctl set -10", false)
153 | end, { description = "decrease brightness by 10%", group = "hotkeys" }),
154 | awful.key({}, "XF86AudioLowerVolume", function()
155 | require("bar.modules.volume.wireplumber").decVol(5)
156 | end, { description = "volume down", group = "hotkeys" }),
157 | awful.key({}, "XF86AudioRaiseVolume", function()
158 | require("bar.modules.volume.wireplumber").incVol(5)
159 | end, { description = "volume up", group = "hotkeys" }),
160 | awful.key({}, "XF86AudioMute", function()
161 | awful.spawn("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle", false)
162 | end, { description = "toggle mute", group = "hotkeys" }),
163 | awful.key({}, "XF86AudioNext", function()
164 | awful.spawn("mpc next", false)
165 | end, { description = "next music", group = "hotkeys" }),
166 | awful.key({}, "XF86AudioPrev", function()
167 | awful.spawn("mpc prev", false)
168 | end, { description = "previous music", group = "hotkeys" }),
169 | awful.key({}, "XF86AudioPlay", function()
170 | awful.spawn("mpc toggle", false)
171 | end, { description = "play/pause music", group = "hotkeys" }),
172 | })
173 |
174 | -- ────────────────────────────────────────────────────────────
175 |
176 | client.connect_signal("request::default_mousebindings", function()
177 | awful.mouse.append_client_mousebindings({
178 | awful.button({}, 1, function(c)
179 | c:activate({ context = "mouse_click" })
180 | end),
181 | awful.button({ modkey }, 1, function(c)
182 | c:activate({ context = "mouse_click", action = "mouse_move" })
183 | end),
184 | awful.button({ modkey }, 3, function(c)
185 | c:activate({ context = "mouse_click", action = "mouse_resize" })
186 | end),
187 | })
188 | end)
189 |
190 | client.connect_signal("request::default_keybindings", function()
191 | awful.keyboard.append_client_keybindings({
192 | awful.key({ modkey }, "f", function(c)
193 | c.fullscreen = not c.fullscreen
194 | c:raise()
195 | end, { description = "toggle fullscreen", group = "client" }),
196 |
197 | awful.key({ modkey }, "q", function(c)
198 | c:kill()
199 | end, { description = "close", group = "client" }),
200 |
201 | awful.key({ modkey }, "F2", function(c)
202 | awful.spawn.easy_async_with_shell(
203 | '/usr/bin/i3lock -i ~/wallpapers/home/turbo.jpg -nfe --fill --force-clock --indicator --verif-text="" --wrong-text="" --noinput-text=""'
204 | )
205 | end, { description = "close", group = "client" }),
206 |
207 | awful.key({ modkey }, "t", awful.client.floating.toggle, { description = "toggle floating", group = "client" }),
208 |
209 | awful.key({ modkey }, "t", function(c)
210 | c.ontop = not c.ontop
211 | end, { description = "toggle keep on top", group = "client" }),
212 | })
213 | end)
214 |
--------------------------------------------------------------------------------
/notifications.lua:
--------------------------------------------------------------------------------
1 | local naughty = require("naughty")
2 |
3 | -- Check if awesome encountered an error during startup and fell back to
4 | -- another config (This code will only ever execute for the fallback config)
5 | naughty.connect_signal("request::display_error", function(message, startup)
6 | naughty.notification({
7 | urgency = "critical",
8 | title = "Oops, an error happened" .. (startup and " during startup!" or "!"),
9 | message = message,
10 | })
11 | end)
12 |
--------------------------------------------------------------------------------
/rc.lua:
--------------------------------------------------------------------------------
1 | ---@diagnostic disable: different-requires
2 | local beautiful = require("beautiful")
3 | local theme = require("theme")
4 |
5 | pcall(require, "luarocks.loader")
6 |
7 | beautiful.init(theme)
8 |
9 | local hostname
10 | local f = io.open("/proc/sys/kernel/hostname", "r")
11 | if f then
12 | hostname = f:read("*l")
13 | f:close()
14 | end
15 |
16 | if hostname == "thinkpad" then
17 | _G.laptop = true
18 | else
19 | _G.laptop = false
20 | end
21 |
22 | require("awful.autofocus")
23 | require("awful.hotkeys_popup.keys")
24 | require("keys")
25 | require("rules")
26 | require("notifications")
27 | require("autostart")
28 | require("wallpaper")
29 |
30 | -- vertical or horizontal
31 | require("bar.init").setup({})
32 |
33 | require("awful").screen.set_auto_dpi_enabled(false)
34 |
35 | -- Enable sloppy focus, so that focus follows mouse.
36 | client.connect_signal("mouse::enter", function(c)
37 | c:activate({ context = "mouse_enter", raise = false })
38 | end)
39 |
40 | -- dont maximize clients automatically on open
41 | client.connect_signal("request::manage", function(c)
42 | c.maximized = false
43 | c.maximized_horizontal = false
44 | c.maximized_vertical = false
45 | -- make floating windows always ontop
46 | if c.floating then
47 | c.ontop = true
48 | end
49 | end)
50 |
51 | ---@diagnostic disable-next-line: param-type-mismatch
52 | collectgarbage("setpause", 110)
53 | ---@diagnostic disable-next-line: param-type-mismatch
54 | collectgarbage("setstepmul", 1000)
55 |
--------------------------------------------------------------------------------
/rules.lua:
--------------------------------------------------------------------------------
1 | local ruled = require("ruled")
2 | local awful = require("awful")
3 |
4 | -- Rules to apply to new clients.
5 | ruled.client.connect_signal("request::rules", function()
6 | -- All clients will match this rule.
7 | ruled.client.append_rule({
8 | id = "global",
9 | rule = {},
10 | properties = {
11 | focus = awful.client.focus.filter,
12 | raise = true,
13 | screen = awful.screen.preferred,
14 | placement = awful.placement.no_overlap + awful.placement.no_offscreen,
15 | },
16 | })
17 |
18 | -- Floating clients.
19 | ruled.client.append_rule({
20 | id = "floating",
21 | rule_any = {
22 | instance = {
23 | "copyq",
24 | "pinentry",
25 | "update_installer",
26 | },
27 | class = {
28 | "Blueman-manager",
29 | "Gpick",
30 | "Sxiv",
31 | "Wpa_gui",
32 | "org.kde.ark",
33 | "veracrypt",
34 | "gnome-calculator",
35 | "pavucontrol",
36 | "gnome-calendar",
37 | "gnome-power-statistics",
38 | "nm-connection-editor",
39 | "Lxappearance",
40 | },
41 | -- Note that the name property shown in xprop might be set slightly after creation of the client
42 | -- and the name shown there might not match defined rules here.
43 | name = {
44 | "Event Tester", -- xev.
45 | "Steam Guard",
46 | },
47 | role = {
48 | "AlarmWindow", -- Thunderbird's calendar.
49 | "ConfigManager", -- Thunderbird's about:config.
50 | "pop-up", -- e.g. Google Chrome's (detached) Developer Tools.
51 | "GtkFileChooserDialog",
52 | "dialog",
53 | "menu",
54 | "task_dialog",
55 | "bubble",
56 | "Preferences",
57 | },
58 | },
59 | properties = { floating = true },
60 | })
61 |
62 | -- Set Firefox to always map on the tag named "2" on screen 1.
63 | ruled.client.append_rule({
64 | rule = { class = { "krita", "xournalpp" } },
65 | properties = { tag = " ", switch_to_tags = true },
66 | })
67 |
68 | ruled.client.append_rule({
69 | rule = {
70 | class = { "steamwebhelper", "steam", "Steam" },
71 | name = { "Steam" },
72 | },
73 | properties = { tag = " ", switch_to_tags = true },
74 | })
75 |
76 | ruled.client.append_rule({
77 | rule = { class = "dolphin" },
78 | properties = { tag = " ", switch_to_tags = true },
79 | })
80 | end)
81 |
--------------------------------------------------------------------------------
/theme/gtk.lua:
--------------------------------------------------------------------------------
1 | local beautiful = require("beautiful")
2 | local gtk = beautiful.gtk.get_theme_variables()
3 |
4 | local colors = {
5 | font_size = gtk.font_size,
6 | font_family = gtk.font_family,
7 | bg_color = gtk.bg_color,
8 | fg_color = gtk.fg_color,
9 | base_color = gtk.base_color,
10 | text_color = gtk.text_color,
11 | button_bg_color = gtk.button_bg_color,
12 | button_fg_color = gtk.button_fg_color,
13 | button_border_color = gtk.button_border_color,
14 | button_border_radius = gtk.button_border_radius,
15 | button_border_width = gtk.button_border_width,
16 | selected_bg_color = gtk.selected_bg_color,
17 | selected_fg_color = gtk.selected_fg_color,
18 | menubar_bg_color = gtk.menubar_bg_color,
19 | menubar_fg_color = gtk.menubar_fg_color,
20 | header_button_bg_color = gtk.header_button_bg_color,
21 | header_button_fg_color = gtk.header_button_fg_color,
22 | header_button_border_color = gtk.header_button_border_color,
23 | error_color = gtk.error_color,
24 | error_bg_color = gtk.error_bg_color,
25 | error_fg_color = gtk.error_fg_color,
26 | warning_color = gtk.warning_color,
27 | warning_bg_color = gtk.warning_bg_color,
28 | warning_fg_color = gtk.warning_fg_color,
29 | success_color = gtk.success_color,
30 | success_bg_color = gtk.success_bg_color,
31 | success_fg_color = gtk.success_fg_color,
32 | tooltip_bg_color = gtk.tooltip_bg_color,
33 | tooltip_fg_color = gtk.tooltip_fg_color,
34 | osd_bg_color = gtk.osd_bg_color,
35 | osd_fg_color = gtk.osd_fg_color,
36 | osd_border_color = gtk.osd_border_color,
37 | wm_bg_color = gtk.wm_bg_color,
38 | wm_border_focused_color = gtk.wm_border_focused_color,
39 | wm_border_unfocused_color = gtk.wm_border_unfocused_color,
40 | wm_title_focused_color = gtk.wm_title_focused_color,
41 | wm_title_unfocused_color = gtk.wm_title_unfocused_color,
42 | wm_icons_focused_color = gtk.wm_icons_focused_color,
43 | wm_icons_unfocused_color = gtk.wm_icons_unfocused_color,
44 | }
45 |
46 | return colors
47 |
--------------------------------------------------------------------------------
/theme/init.lua:
--------------------------------------------------------------------------------
1 | ---------------------------
2 | -- Default awesome theme --
3 | ---------------------------
4 |
5 | local theme_assets = require("beautiful.theme_assets")
6 | local xresources = require("beautiful.xresources")
7 | local rnotification = require("ruled.notification")
8 | local dpi = xresources.apply_dpi
9 | local gears = require("gears")
10 |
11 | local gfs = require("gears.filesystem")
12 | local themes_path = gfs.get_themes_dir()
13 | local pallete = require("theme.gtk")
14 |
15 | local theme = {}
16 |
17 | theme.font = pallete.font_family
18 | theme.font_alt = pallete.font_family
19 | theme.font_size = pallete.font_size
20 | theme.icon_font = "Symbols Nerd Font Mono"
21 |
22 | theme.bg_normal = pallete.bg_color
23 | theme.bg_focus = pallete.selected_bg_color
24 | theme.bg_warning = pallete.warning_bg_color
25 | theme.bg_urgent = pallete.error_bg_color
26 | theme.bg_success = pallete.success_bg_color
27 | theme.bg_minimize = pallete.tooltip_bg_color
28 |
29 | theme.accent = pallete.selected_bg_color
30 |
31 | theme.module_bg = pallete.button_bg_color
32 | theme.module_bg_focused = pallete.selected_bg_color
33 |
34 | theme.bg_systray = pallete.bg_color
35 | theme.systray_icon_spacing = dpi(4)
36 |
37 | theme.fg_normal = pallete.fg_color
38 | theme.fg_focus = pallete.selected_fg_color
39 | theme.fg_warning = pallete.warning_fg_color
40 | theme.fg_urgent = pallete.error_fg_color
41 | theme.fg_success = pallete.success_fg_color
42 | theme.fg_minimize = pallete.tooltip_fg_color
43 |
44 | -- theme.useless_gap = dpi(4)
45 | theme.gap_single_client = true
46 | theme.border_width = dpi(2)
47 | theme.border_color_normal = pallete.wm_border_unfocused_color
48 | theme.border_color_active = pallete.wm_border_focused_color
49 | theme.border_color_marked = theme.accent
50 |
51 | theme.taglist_bg_empty = "#00000000"
52 | theme.taglist_bg_occupied = pallete.button_bg_color
53 | theme.taglist_bg_urgent = "#00000000"
54 | theme.taglist_bg_focus = pallete.selected_bg_color
55 | theme.taglist_font = theme.font .. " " .. pallete.font_size
56 | theme.taglist_fg_focus = pallete.selected_fg_color
57 | theme.taglist_fg_occupied = pallete.osd_fg_color
58 | theme.taglist_fg_urgent = pallete.error_fg_color
59 | theme.taglist_fg_empty = pallete.button_fg_color
60 | theme.taglist_shape = function(cx, width, height)
61 | gears.shape.rounded_rect(cx, width, height, dpi(6))
62 | end
63 |
64 | theme.tasklist_bg_normal = theme.module_bg
65 | theme.tasklist_bg_focus = theme.accent
66 | theme.tasklist_bg_urgent = theme.module_bg_focused
67 | theme.tasklist_fg_urgent = pallete.error_fg_color
68 |
69 | theme.menu_font = theme.font_alt .. " 9"
70 | theme.menu_height = dpi(20)
71 | theme.menu_width = dpi(100)
72 | theme.menu_border_color = pallete.wm_border_unfocused_color
73 | theme.menu_border_width = dpi(2)
74 | theme.menu_fg_focus = pallete.selected_fg_color
75 | theme.menu_bg_focus = pallete.selected_bg_color
76 | theme.menu_fg_normal = pallete.menubar_fg_color
77 | theme.menu_bg_normal = pallete.menubar_bg_color
78 |
79 | -- Variables set for theming notifications:
80 | -- notification_font
81 | -- notification_[bg|fg]
82 | -- notification_[width|height|margin]
83 | -- notification_[border_color|border_width|shape|opacity]
84 | theme.notification_bg = pallete.tooltip_bg_color
85 | theme.notification_fg = pallete.tooltip_fg_color
86 | theme.notification_width = dpi(360)
87 | theme.notification_margin = dpi(120)
88 | theme.notification_border_width = dpi(2)
89 | theme.notification_border_color = pallete.wm_border_unfocused_color
90 | theme.notification_border_shape = function(cx, w, h)
91 | gears.shape.rounded_rect(cx, w, h, dpi(12))
92 | end
93 | theme.notification_icon_size = dpi(100)
94 |
95 | -- Variables set for theming the menu:
96 | -- menu_[bg|fg]_[normal|focus]
97 | -- menu_[border_color|border_width]
98 |
99 | -- You can add as many variables as
100 | -- you wish and access them by using
101 | -- beautiful.variable in your rc.lua
102 | --theme.bg_widget = "#cc0000"
103 |
104 | ---@diagnostic disable-next-line: param-type-mismatch
105 | theme.pfp = gfs.get_configuration_dir() .. "assets/pfp.gif"
106 | theme.cover_art = gfs.get_configuration_dir() .. "assets/albumart.jpg"
107 |
108 | local layouts = {
109 | layout_fairh = themes_path .. "default/layouts/fairhw.png",
110 | layout_fairv = themes_path .. "default/layouts/fairvw.png",
111 | layout_floating = themes_path .. "default/layouts/floatingw.png",
112 | layout_magnifier = themes_path .. "default/layouts/magnifierw.png",
113 | layout_max = themes_path .. "default/layouts/maxw.png",
114 | layout_fullscreen = themes_path .. "default/layouts/fullscreenw.png",
115 | layout_tilebottom = themes_path .. "default/layouts/tilebottomw.png",
116 | layout_tileleft = themes_path .. "default/layouts/tileleftw.png",
117 | layout_tile = themes_path .. "default/layouts/tilew.png",
118 | layout_tiletop = themes_path .. "default/layouts/tiletopw.png",
119 | layout_spiral = themes_path .. "default/layouts/spiralw.png",
120 | layout_dwindle = themes_path .. "default/layouts/dwindlew.png",
121 | layout_cornernw = themes_path .. "default/layouts/cornernww.png",
122 | layout_cornerne = themes_path .. "default/layouts/cornernew.png",
123 | layout_cornersw = themes_path .. "default/layouts/cornersww.png",
124 | layout_cornerse = themes_path .. "default/layouts/cornersew.png",
125 | }
126 |
127 | -- You can use your own layout icons like this:
128 | theme.layout_tile = gears.color.recolor_image(layouts.layout_tile, theme.accent)
129 | theme.layout_floating = gears.color.recolor_image(layouts.layout_floating, theme.accent)
130 | theme.layout_tiletop = gears.color.recolor_image(layouts.layout_tiletop, theme.accent)
131 |
132 | -- Generate Awesome icon:
133 | theme.awesome_icon = theme_assets.awesome_icon(theme.menu_height, theme.bg_focus, theme.fg_focus)
134 |
135 | -- Define the icon theme for application icons. If not set then the icons
136 | -- from /usr/share/icons and /usr/share/icons/hicolor will be used.
137 | theme.icon_theme = "Papirus-Dark"
138 |
139 | -- Set different colors for urgent notifications.
140 | rnotification.connect_signal("request::rules", function()
141 | rnotification.append_rule({
142 | rule = { urgency = "critical" },
143 | properties = { bg = pallete.error_bg_color, fg = pallete.error_fg_color },
144 | })
145 | end)
146 |
147 | return theme
148 |
--------------------------------------------------------------------------------
/wallpaper.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 | local wibox = require("wibox")
4 | local dpi = require("beautiful").xresources.apply_dpi
5 |
6 | -- Slideshow
7 | ---@diagnostic disable-next-line: undefined-global
8 | screen.connect_signal("request::wallpaper", function(s)
9 | awful.wallpaper({
10 | screen = s,
11 | widget = {
12 | {
13 | horizontal_fit_policy = "fit",
14 | vertical_fit_policy = "fit",
15 | image = gears.surface.crop_surface({
16 | surface = gears.surface.load_uncached(
17 | gears.filesystem.get_random_file_from_dir(
18 | os.getenv("HOME") .. "/wallpapers/",
19 | { ".jpg", ".png", ".svg" },
20 | true
21 | )
22 | ),
23 | ratio = s.geometry.width / s.geometry.height,
24 | }),
25 | resize = true,
26 | widget = wibox.widget.imagebox,
27 | },
28 | valign = "center",
29 | halign = "center",
30 | tiled = false,
31 | widget = wibox.container.tile,
32 | },
33 | })
34 | end)
35 |
36 | gears.timer({
37 | timeout = 300,
38 | autostart = true,
39 | callback = function()
40 | ---@diagnostic disable-next-line: undefined-global
41 | for s in screen do
42 | s:emit_signal("request::wallpaper")
43 | end
44 | end,
45 | })
46 |
--------------------------------------------------------------------------------