Lua · 15829 bytes Raw Blame History
1 -- gar default configuration
2 -- Copy to ~/.config/gar/init.lua to customize
3
4 -- Autostart applications (only run once per session)
5 -- gar.exec_once("random-bg cold") -- Pick random wallpaper from a themed directory
6 -- garbg daemon is managed by systemd (garbg.service) with internal X11 retry
7 gar.exec_once("garbg set ~/Pictures/background/cold/ --random")
8
9 -- Screen locker daemon
10 gar.exec_once("garlock daemon")
11
12 -- Clipboard manager daemon
13 gar.exec_once("garclip daemon --foreground")
14
15 -- System tray with quick settings panel
16 gar.exec_once("gartray daemon")
17
18 -- Polkit authentication agent (needed for power actions via D-Bus)
19 gar.exec_once("garcard daemon")
20
21 -- External fallback authentication agents (optional):
22 -- gar.exec_once("/usr/libexec/kf6/polkit-kde-authentication-agent-1")
23 -- gar.exec_once("/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1")
24
25 -- Uncomment the ones you want:
26 -- gar.exec_once("picom") -- Compositor (for transparency/shadows)
27 -- gar.exec_once("dunst") -- Notification daemon
28 -- gar.exec_once("nm-applet") -- NetworkManager tray icon
29 -- gar.exec_once("blueman-applet") -- Bluetooth tray icon
30 -- gar.exec_once("xss-lock -- i3lock -c 000000") -- Auto-lock on suspend
31
32 -- Appearance
33 gar.set("border_width", 2)
34 gar.set("border_color_focused", "#5294e2")
35 gar.set("border_color_unfocused", "#2d2d2d")
36 gar.set("gap_inner", 12)
37 gar.set("gap_outer", 16)
38
39 -- Visual Effects (picom compositor)
40 -- gar auto-generates ~/.config/gar/picom.conf from these settings
41 -- Changes take effect on reload (Mod+Shift+R) - picom is signaled automatically
42 gar.set("corner_radius", 18) -- 0 = square corners
43 gar.set("blur_enabled", true)
44 gar.set("blur_method", "dual_kawase") -- "gaussian", "dual_kawase", "box"
45 gar.set("blur_strength", 5) -- 1-20 for dual_kawase
46 gar.set("shadow_enabled", true)
47 gar.set("shadow_radius", 12)
48 gar.set("shadow_opacity", 0.75)
49 gar.set("shadow_offset_x", -7)
50 gar.set("shadow_offset_y", -7)
51 gar.set("opacity_focused", 0.94)
52 gar.set("opacity_unfocused", 0.6) -- Set to 0.9 to dim unfocused windows
53 gar.set("fade_enabled", true)
54 gar.set("fade_delta", 3)
55
56 -- Title bars (disabled by default)
57 -- gar.set("titlebar_enabled", true)
58 -- gar.set("titlebar_height", 20)
59 -- gar.set("titlebar_color_focused", "#3d3d3d")
60 -- gar.set("titlebar_color_unfocused", "#2d2d2d")
61 -- gar.set("titlebar_text_color", "#ffffff")
62
63 -- Border gradients (purple to blue theme)
64 -- direction options: "vertical", "horizontal", "diagonal"
65 gar.set("border_gradient_enabled", true)
66 gar.set("border_gradient_start_focused", "#9b59b6") -- Purple
67 gar.set("border_gradient_end_focused", "#3498db") -- Blue
68 gar.set("border_gradient_start_unfocused", "#4a235a") -- Dark purple
69 gar.set("border_gradient_end_unfocused", "#1a5276") -- Dark blue
70 gar.set("border_gradient_direction", "diagonal")
71
72 -- Animations (picom v12+)
73 -- open options: "slide-in", "fly-in", "appear", "none"
74 -- close options: "slide-out", "fly-out", "disappear", "none"
75 gar.set("animation_open", "appear")
76 gar.set("animation_close", "disappear")
77 gar.set("animation_duration", 0.15) -- seconds
78
79 -- Custom GLSL shader (picom, requires GLX backend)
80 -- Bundled shaders: ~/.config/gar/shaders/focused-glow.glsl, dim-unfocused.glsl
81 -- gar.set("picom_shader", "~/.config/gar/shaders/focused-glow.glsl")
82
83 -- Per-window picom rules
84 -- NOTE: When using rules, picom ignores old-style options (inactive-opacity, shadow-exclude, etc.)
85 -- Only enable rules if you want full control via rules-based config
86 -- Available rule options: corner_radius, opacity, shadow, blur_background, shader
87 -- Match syntax: class_g = 'ClassName', class_i = 'instance', name = 'title', window_type = 'type'
88 -- gar.picom_rule({
89 -- match = "class_g = 'Alacritty'",
90 -- blur_background = true,
91 -- opacity = 0.92,
92 -- })
93 -- gar.picom_rule({
94 -- match = "class_g = 'firefox'",
95 -- corner_radius = 8,
96 -- })
97
98 -- garbar status bar (native gar integration)
99 -- When this table is present, gar automatically spawns and manages garbar
100 gar.bar = {
101 height = 32,
102 position = "top",
103 background = "#1a1a1a",
104 foreground = "#ffffff",
105 opacity = 1.0,
106 fonts = {
107 "JetBrainsMono Nerd Font:size=10",
108 "Symbols Nerd Font:size=10",
109 },
110 padding = { left = 8, right = 16, top = 0, bottom = 0 },
111
112 -- Module layout
113 modules_left = { "workspaces", "window_title" },
114 modules_center = {},
115 modules_right = { "almanta", "filesystem", "memory", "cpu", "battery", "wlan", "volume", "tray", "datetime", "quick_settings" },
116
117 -- Module configurations
118 modules = {
119 workspaces = {
120 font_size = 11,
121 focused = {
122 foreground = "#ffffff",
123 background = "transparent",
124 underline = { width = 2, color = "#33ccff" },
125 },
126 unfocused = {
127 foreground = "#666666",
128 background = "transparent",
129 },
130 urgent = {
131 foreground = "#ffffff",
132 background = "#ff5555",
133 },
134 },
135 window_title = {
136 max_length = 50,
137 empty_text = "Desktop",
138 },
139 datetime = {
140 format = " %a %b %d  %H:%M",
141 },
142 cpu = {
143 format = " {usage}%",
144 },
145 memory = {
146 format = " {percent}%",
147 },
148 battery = {
149 device = "auto",
150 format_charging = " {percent}%",
151 format_discharging = " {percent}%",
152 format_full = " Full",
153 },
154 -- Script modules (custom commands)
155 script = {
156 almanta = {
157 exec = [[
158 HOST="espadon@almanta"
159 SSH_OPTS="-T -o BatchMode=yes -o ConnectTimeout=2 -o ConnectionAttempts=1"
160 if ! ssh $SSH_OPTS "$HOST" "echo ok" >/dev/null 2>&1; then
161 echo "almanta: down"
162 exit 0
163 fi
164 load=$(ssh $SSH_OPTS "$HOST" "cut -d' ' -f1 /proc/loadavg" 2>/dev/null || echo "?")
165 mem=$(ssh $SSH_OPTS "$HOST" "free | grep '^Mem:' | awk '{printf \"%.0f\", (\$3/\$2)*100}'" 2>/dev/null || echo "?")
166 disk=$(ssh $SSH_OPTS "$HOST" "df / | tail -1 | awk '{print \$5}' | tr -d '%'" 2>/dev/null || echo "?")
167 echo "almanta L:$load M:$mem% D:$disk%"
168 ]],
169 interval = 30,
170 },
171 filesystem = {
172 exec = [[df -h / | awk 'NR==2 {print " " $4}']],
173 interval = 60,
174 },
175 wlan = {
176 exec = [[
177 IFACE="wlp1s0f0"
178 if [ -d "/sys/class/net/$IFACE" ]; then
179 STATE=$(cat /sys/class/net/$IFACE/operstate 2>/dev/null)
180 if [ "$STATE" = "up" ]; then
181 ESSID=$(iwgetid -r 2>/dev/null || echo "")
182 IP=$(ip -4 addr show $IFACE 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1)
183 if [ -n "$ESSID" ]; then
184 echo " $ESSID $IP"
185 else
186 echo " connected"
187 fi
188 else
189 echo " offline"
190 fi
191 else
192 echo " N/A"
193 fi
194 ]],
195 interval = 5,
196 },
197 volume = {
198 exec = [[
199 if command -v pamixer >/dev/null 2>&1; then
200 if pamixer --get-mute | grep -q true; then
201 echo " muted"
202 else
203 VOL=$(pamixer --get-volume)
204 echo " $VOL%"
205 fi
206 elif command -v pactl >/dev/null 2>&1; then
207 VOL=$(pactl get-sink-volume @DEFAULT_SINK@ | grep -oP '\d+%' | head -1)
208 MUTE=$(pactl get-sink-mute @DEFAULT_SINK@ | grep -oP 'yes|no')
209 if [ "$MUTE" = "yes" ]; then
210 echo " muted"
211 else
212 echo " $VOL"
213 fi
214 else
215 echo " N/A"
216 fi
217 ]],
218 interval = 1,
219 },
220 },
221 },
222 }
223
224 -- Behavior
225 gar.set("follow_window_on_move", true) -- Follow window when using Mod+Shift+number
226
227 -- Mod key: "mod" = Super/Win, "alt" = Alt
228 -- Use "mod" for real X session, "alt" for nested testing (Xephyr)
229 local mod = "mod"
230
231 -- Terminal (fallback)
232 gar.bind(mod .. "+Return", function()
233 gar.exec("alacritty || kitty || foot || xterm")
234 end)
235
236 -- garterm with fish shell
237 gar.bind(mod .. "+shift+Return", function()
238 gar.exec("garterm")
239 end)
240
241 -- CPR-Music dev workspace via session
242 gar.bind(mod .. "+alt+Return", function()
243 gar.exec("garterm")
244 gar.exec("sleep 0.3 && gartermctl load-session cpr-music")
245 end)
246
247 --------------------------------------------------------------------------------
248 -- GARTERM CONFIGURATION
249 --------------------------------------------------------------------------------
250 -- garterm reads this table for shell, font, colors, sessions, and keybinds
251
252 gar.terminal = {
253 -- Default shell
254 shell = "/usr/bin/fish",
255
256 -- Font settings
257 font = {
258 family = "JetBrainsMono Nerd Font",
259 size = 20.0,
260 },
261
262 -- Color scheme
263 colors = {
264 preset = "catpuccin-mocha",
265 },
266
267 -- Tab bar configuration
268 tab_bar = {
269 height = 24, -- Tab bar height in pixels
270 position = "top", -- "top" or "bottom"
271 show_single_tab = false, -- Show tab bar even with one tab
272 max_tab_width = 200, -- Maximum width per tab in pixels
273 tab_padding = 16, -- Horizontal padding inside each tab
274 shorten_paths = true, -- Shorten paths: ~/P/a/src style
275
276 -- Colors as {R, G, B, A} with values 0.0 to 1.0
277 background = {0.08, 0.08, 0.12, 1.0}, -- Tab bar background
278 active_bg = {0.15, 0.15, 0.20, 1.0}, -- Active tab background
279 inactive_bg = {0.10, 0.10, 0.14, 1.0},-- Inactive tab background
280 active_fg = {1.0, 1.0, 1.0, 1.0}, -- Active tab text color
281 inactive_fg = {0.7, 0.7, 0.7, 1.0}, -- Inactive tab text color
282 },
283
284 -- Session definitions (load with gartermctl load-session <name>)
285 sessions = {
286 -- CPR-Music development workspace
287 ["cpr-music"] = {
288 tabs = {
289 {
290 title = "Frontend",
291 cwd = "~/GithubOrgs/espadonne/CPR-Music",
292 cmd = "npm run dev",
293 },
294 {
295 title = "Backend",
296 cwd = "~/GithubOrgs/mfwolffe/CPR-Music-Backend",
297 cmd = "source .venv/bin/activate.fish && python manage.py runserver",
298 },
299 {
300 title = "Editor",
301 cwd = "~/GithubOrgs/espadonne/CPR-Music",
302 cmd = "fackr",
303 },
304 },
305 },
306 },
307
308 -- Keybinds within garterm (Lua function callbacks)
309 keybinds = {
310 -- Quick session loading
311 ["alt+m"] = { action = "load_session", session = "cpr-music"},
312
313 -- Open fackr in current pane
314 ["alt+e"] = function()
315 gar.terminal.send_text(nil, "fackr \n")
316 end,
317
318 -- Open fackr in new tab
319 ["alt+shift+e"] = function()
320 gar.terminal.new_tab({})
321 gar.terminal.send_text(nil, "fackr \n")
322 end,
323 },
324 }
325
326 -- Close window
327 gar.bind(mod .. "+q", gar.close_window)
328
329 -- Reload config
330 gar.bind(mod .. "+shift+r", gar.reload)
331
332 -- Exit gar (return to display manager)
333 gar.bind(mod .. "+shift+e", gar.exit)
334
335 -- Focus navigation (arrow keys)
336 gar.bind(mod .. "+Left", gar.focus("left"))
337 gar.bind(mod .. "+Right", gar.focus("right"))
338 gar.bind(mod .. "+Up", gar.focus("up"))
339 gar.bind(mod .. "+Down", gar.focus("down"))
340
341 -- Focus navigation (vim keys)
342 gar.bind(mod .. "+h", gar.focus("left"))
343 gar.bind(mod .. "+l", gar.focus("right"))
344 gar.bind(mod .. "+k", gar.focus("up"))
345 gar.bind(mod .. "+j", gar.focus("down"))
346
347 -- Swap windows
348 gar.bind(mod .. "+shift+Left", gar.swap("left"))
349 gar.bind(mod .. "+shift+Right", gar.swap("right"))
350 gar.bind(mod .. "+shift+Up", gar.swap("up"))
351 gar.bind(mod .. "+shift+Down", gar.swap("down"))
352
353 gar.bind(mod .. "+shift+h", gar.swap("left"))
354 gar.bind(mod .. "+shift+l", gar.swap("right"))
355 gar.bind(mod .. "+shift+k", gar.swap("up"))
356 gar.bind(mod .. "+shift+j", gar.swap("down"))
357
358 -- Resize
359 gar.bind(mod .. "+ctrl+Left", gar.resize("left", 0.05))
360 gar.bind(mod .. "+ctrl+Right", gar.resize("right", 0.05))
361 gar.bind(mod .. "+ctrl+Up", gar.resize("up", 0.05))
362 gar.bind(mod .. "+ctrl+Down", gar.resize("down", 0.05))
363
364 gar.bind(mod .. "+ctrl+h", gar.resize("left", 0.05))
365 gar.bind(mod .. "+ctrl+l", gar.resize("right", 0.05))
366 gar.bind(mod .. "+ctrl+k", gar.resize("up", 0.05))
367 gar.bind(mod .. "+ctrl+j", gar.resize("down", 0.05))
368
369 -- Equalize splits
370 gar.bind(mod .. "+e", gar.equalize)
371
372 -- Toggle floating
373 gar.bind(mod .. "+f", gar.toggle_floating)
374
375 -- Toggle fullscreen
376 gar.bind(mod .. "+shift+f", gar.toggle_fullscreen)
377
378 -- Cycle through floating windows
379 gar.bind(mod .. "+grave", gar.cycle_floating) -- Mod+` (backtick)
380
381 -- Workspaces
382 for i = 1, 9 do
383 gar.bind(mod .. "+" .. i, gar.workspace(i))
384 gar.bind(mod .. "+shift+" .. i, gar.move_to_workspace(i))
385 end
386 gar.bind(mod .. "+0", gar.workspace(10))
387 gar.bind(mod .. "+shift+0", gar.move_to_workspace(10))
388
389 -- Multi-monitor (comma/period = prev/next)
390 gar.bind(mod .. "+comma", gar.focus_monitor("prev"))
391 gar.bind(mod .. "+period", gar.focus_monitor("next"))
392 gar.bind(mod .. "+shift+comma", gar.move_to_monitor("prev"))
393 gar.bind(mod .. "+shift+period", gar.move_to_monitor("next"))
394
395 -- Workspace cycling
396 gar.bind(mod .. "+Tab", gar.workspace_next())
397 gar.bind(mod .. "+shift+Tab", gar.workspace_prev())
398
399 -- Launchers (garlaunch)
400 local garlaunch = os.getenv("HOME") .. "/.cargo/bin/garlaunch"
401 gar.bind(mod .. "+space", function()
402 gar.exec(garlaunch .. " --mode drun 2>/tmp/garlaunch-debug.log")
403 end)
404 gar.bind(mod .. "+shift+space", function()
405 gar.exec(garlaunch .. " --mode window 2>/tmp/garlaunch-debug.log")
406 end)
407 gar.bind(mod .. "+r", function()
408 gar.exec(garlaunch .. " --mode run 2>/tmp/garlaunch-debug.log")
409 end)
410
411 -- dmenu alternative (if rofi not available)
412 gar.bind(mod .. "+p", function()
413 gar.exec("dmenu_run")
414 end)
415
416 -- Screenshot (requires scrot or maim)
417 gar.bind("Print", function()
418 gar.exec("scrot -e 'mv $f ~/Pictures/' || maim ~/Pictures/screenshot-$(date +%s).png")
419 end)
420 gar.bind(mod .. "+Print", function()
421 gar.exec("scrot -s -e 'mv $f ~/Pictures/' || maim -s ~/Pictures/screenshot-$(date +%s).png")
422 end)
423
424 -- Screenshot (garshot)
425 gar.bind("ctrl+shift+3", function()
426 gar.exec("garshot select --annotate") -- Region selection with annotation editor
427 end)
428 gar.bind("ctrl+shift+4", function()
429 gar.exec("garshot select") -- Interactive region selection with blur overlay
430 end)
431 gar.bind("ctrl+shift+5", function()
432 gar.exec("garshot screen") -- Full screen capture
433 end)
434 gar.bind("ctrl+shift+6", function()
435 gar.exec("garshot window") -- Active window capture
436 end)
437
438 -- Slideshow mode (Mod+Shift+P to avoid conflict with dmenu on Mod+P)
439 gar.bind(mod .. "+shift+p", function() gar.exec("garbg set ~/Pictures/background/cold/ --random --interval 2m") end)
440
441 -- Lock screen (requires i3lock, swaylock, or slock)
442 gar.bind(mod .. "+Escape", function()
443 gar.exec("i3lock -c 000000 || swaylock -c 000000 || slock")
444 end)
445
446 -- Lock screen with garlock (via daemon)
447 gar.bind(mod .. "+shift+q", function()
448 gar.exec("garlock lock")
449 end)
450
451 -- Volume controls (requires pactl/pamixer)
452 gar.bind("XF86AudioRaiseVolume", function()
453 gar.exec("pactl set-sink-volume @DEFAULT_SINK@ +5% || pamixer -i 5")
454 end)
455 gar.bind("XF86AudioLowerVolume", function()
456 gar.exec("pactl set-sink-volume @DEFAULT_SINK@ -5% || pamixer -d 5")
457 end)
458 gar.bind("XF86AudioMute", function()
459 gar.exec("pactl set-sink-mute @DEFAULT_SINK@ toggle || pamixer -t")
460 end)
461
462 -- Brightness controls (requires brightnessctl or light)
463 gar.bind("XF86MonBrightnessUp", function()
464 gar.exec("brightnessctl set +10% || light -A 10")
465 end)
466 gar.bind("XF86MonBrightnessDown", function()
467 gar.exec("brightnessctl set 10%- || light -U 10")
468 end)
469
470 -- Clipboard history (garclip)
471 gar.bind(mod .. "+shift+v", function()
472 gar.exec("garclip-picker")
473 end)