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