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