diff --git a/hypr/.config/hypr/hyprland.lua b/hypr/.config/hypr/hyprland.lua index a0e91be..bb41b80 100644 --- a/hypr/.config/hypr/hyprland.lua +++ b/hypr/.config/hypr/hyprland.lua @@ -20,15 +20,17 @@ hl.monitor({ output = "DP-3", mode = "highres", position = "auto", scale = "auto --------------------- -- Set programs that you use -local terminal = "ghostty" -local fileManager = "nautilus" -local browser = "brave" -local menu = "wofi --show drun" -local pwManager = "bitwarden-desktop" +local terminal = "ghostty" +local multiplexer = os.getenv("HOME") .. "/.local/bin/herdr" +local fileManager = "nautilus" +local browser = "brave" +local menu = "wofi --show drun" +local pwManager = "bitwarden-desktop" -- Hyprland's session PATH is /usr/local/bin:/usr/bin — no ~/.local/bin — -- so user scripts need their absolute path in autostart and binds. -local handyNo = os.getenv("HOME") .. "/.local/bin/handy-no" +local handyNo = os.getenv("HOME") .. "/.local/bin/handy-no" +local handyBootHide = os.getenv("HOME") .. "/.local/bin/handy-boot-hide" ------------------- @@ -43,18 +45,21 @@ hl.on("hyprland.start", function() -- hl.exec_cmd("wl-paste --type image --watch cliphist store") -- Stores only image data hl.exec_cmd("nextcloud --background") - hl.exec_cmd("handy --start-hidden") -- English instance (parakeet) - hl.exec_cmd(handyNo .. " start") -- Norwegian instance (whisper-large-v3), private D-Bus + own data dir + hl.exec_cmd("handy --start-hidden") -- English instance (parakeet) + hl.exec_cmd(handyNo .. " start") -- Norwegian instance (whisper-large-v3), private D-Bus + own data dir + -- Handy ignores --start-hidden when the tray icon is off (it force-shows + -- the window as its only way back in), so hide the first two windows here. + hl.exec_cmd(handyBootHide) -- Launch apps on specific workspaces (window rules below) - hl.exec_cmd("ghostty -e tmux") + hl.exec_cmd("ghostty -e " .. multiplexer) hl.exec_cmd("brave") hl.exec_cmd("steam") end) hl.window_rule({ match = { class = "^(com.mitchellh.ghostty)$" }, workspace = "1 silent" }) -hl.window_rule({ match = { class = "^(brave-browser)$" }, workspace = "2 silent" }) -hl.window_rule({ match = { class = "^(steam)$" }, workspace = "3 silent" }) +hl.window_rule({ match = { class = "^(brave-browser)$" }, workspace = "2 silent" }) +hl.window_rule({ match = { class = "^(steam)$" }, workspace = "3 silent" }) ------------------------------- @@ -75,12 +80,12 @@ hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia") -- Refer to https://wiki.hypr.land/Configuring/Basics/Variables/ hl.config({ general = { - gaps_in = 5, - gaps_out = 20, + gaps_in = 5, + gaps_out = 20, - border_size = 2, + border_size = 2, - col = { + col = { active_border = { colors = { "rgba(33ccffee)", "rgba(00ff99ee)" }, angle = 45 }, inactive_border = "rgba(595959aa)", }, @@ -89,27 +94,27 @@ hl.config({ resize_on_border = true, -- Please see https://wiki.hypr.land/Configuring/Advanced-and-Cool/Tearing/ before you turn this on - allow_tearing = false, + allow_tearing = false, - layout = "dwindle", + layout = "dwindle", }, decoration = { - rounding = 10, - rounding_power = 2, + rounding = 10, + rounding_power = 2, -- Change transparency of focused and unfocused windows active_opacity = 1.0, inactive_opacity = 1.0, - shadow = { + shadow = { enabled = true, range = 4, render_power = 3, color = "rgba(1a1a1aee)", }, - blur = { + blur = { enabled = true, size = 3, passes = 1, @@ -124,27 +129,27 @@ hl.config({ }) -- Default curves and animations, see https://wiki.hypr.land/Configuring/Advanced-and-Cool/Animations/ -hl.curve("easeOutQuint", { type = "bezier", points = { {0.23, 1}, {0.32, 1} } }) -hl.curve("easeInOutCubic", { type = "bezier", points = { {0.65, 0.05}, {0.36, 1} } }) -hl.curve("linear", { type = "bezier", points = { {0, 0}, {1, 1} } }) -hl.curve("almostLinear", { type = "bezier", points = { {0.5, 0.5}, {0.75, 1} } }) -hl.curve("quick", { type = "bezier", points = { {0.15, 0}, {0.1, 1} } }) +hl.curve("easeOutQuint", { type = "bezier", points = { { 0.23, 1 }, { 0.32, 1 } } }) +hl.curve("easeInOutCubic", { type = "bezier", points = { { 0.65, 0.05 }, { 0.36, 1 } } }) +hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } }) +hl.curve("almostLinear", { type = "bezier", points = { { 0.5, 0.5 }, { 0.75, 1 } } }) +hl.curve("quick", { type = "bezier", points = { { 0.15, 0 }, { 0.1, 1 } } }) -hl.animation({ leaf = "global", enabled = true, speed = 10, bezier = "default" }) -hl.animation({ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" }) -hl.animation({ leaf = "windows", enabled = true, speed = 4.79, bezier = "easeOutQuint" }) -hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, bezier = "easeOutQuint", style = "popin 87%" }) -hl.animation({ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" }) -hl.animation({ leaf = "fadeIn", enabled = true, speed = 1.73, bezier = "almostLinear" }) -hl.animation({ leaf = "fadeOut", enabled = true, speed = 1.46, bezier = "almostLinear" }) -hl.animation({ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" }) -hl.animation({ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" }) -hl.animation({ leaf = "layersIn", enabled = true, speed = 4, bezier = "easeOutQuint", style = "fade" }) -hl.animation({ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "linear", style = "fade" }) -hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 1.79, bezier = "almostLinear" }) +hl.animation({ leaf = "global", enabled = true, speed = 10, bezier = "default" }) +hl.animation({ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" }) +hl.animation({ leaf = "windows", enabled = true, speed = 4.79, bezier = "easeOutQuint" }) +hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, bezier = "easeOutQuint", style = "popin 87%" }) +hl.animation({ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" }) +hl.animation({ leaf = "fadeIn", enabled = true, speed = 1.73, bezier = "almostLinear" }) +hl.animation({ leaf = "fadeOut", enabled = true, speed = 1.46, bezier = "almostLinear" }) +hl.animation({ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" }) +hl.animation({ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" }) +hl.animation({ leaf = "layersIn", enabled = true, speed = 4, bezier = "easeOutQuint", style = "fade" }) +hl.animation({ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "linear", style = "fade" }) +hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 1.79, bezier = "almostLinear" }) hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 1.39, bezier = "almostLinear" }) -hl.animation({ leaf = "workspaces", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" }) -hl.animation({ leaf = "workspacesIn", enabled = true, speed = 1.21, bezier = "almostLinear", style = "fade" }) +hl.animation({ leaf = "workspaces", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" }) +hl.animation({ leaf = "workspacesIn", enabled = true, speed = 1.21, bezier = "almostLinear", style = "fade" }) hl.animation({ leaf = "workspacesOut", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" }) -- Ref https://wiki.hypr.land/Configuring/Basics/Workspace-Rules/ @@ -180,17 +185,17 @@ hl.config({ hl.config({ input = { - kb_layout = "us,no", - kb_variant = "", - kb_model = "", - kb_options = "caps:escape,grp:alt_shift_toggle", - kb_rules = "", + kb_layout = "us,no", + kb_variant = "", + kb_model = "", + kb_options = "caps:escape,grp:alt_shift_toggle", + kb_rules = "", follow_mouse = 1, - sensitivity = 0, -- -1.0 - 1.0, 0 means no modification. + sensitivity = 0, -- -1.0 - 1.0, 0 means no modification. - touchpad = { + touchpad = { natural_scroll = false, }, }, @@ -211,14 +216,14 @@ hl.device({ local mainMod = "SUPER" -- Sets "Windows" key as main modifier -- See https://wiki.hypr.land/Configuring/Basics/Binds/ for more -hl.bind(mainMod .. " + C", hl.dsp.window.close()) -hl.bind(mainMod .. " + M", hl.dsp.exec_cmd("uwsm stop")) -- graceful uwsm session teardown instead of hl.dsp.exit() -hl.bind(mainMod .. " + F", hl.dsp.window.fullscreen()) +hl.bind(mainMod .. " + C", hl.dsp.window.close()) +hl.bind(mainMod .. " + M", hl.dsp.exec_cmd("uwsm stop")) -- graceful uwsm session teardown instead of hl.dsp.exit() +hl.bind(mainMod .. " + F", hl.dsp.window.fullscreen()) hl.bind(mainMod .. " + Return", hl.dsp.exec_cmd(terminal)) -hl.bind(mainMod .. " + E", hl.dsp.exec_cmd(fileManager)) -hl.bind(mainMod .. " + space", hl.dsp.exec_cmd(menu)) -hl.bind(mainMod .. " + B", hl.dsp.exec_cmd(browser)) -hl.bind(mainMod .. " + P", hl.dsp.exec_cmd(pwManager)) +hl.bind(mainMod .. " + E", hl.dsp.exec_cmd(fileManager)) +hl.bind(mainMod .. " + space", hl.dsp.exec_cmd(menu)) +hl.bind(mainMod .. " + B", hl.dsp.exec_cmd(browser)) +hl.bind(mainMod .. " + P", hl.dsp.exec_cmd(pwManager)) hl.bind(mainMod .. " + A", hl.dsp.exec_cmd("grimblast copy area")) hl.bind(mainMod .. " + S", hl.dsp.exec_cmd("grimblast save area")) @@ -237,14 +242,18 @@ hl.bind(mainMod .. " + T", hl.dsp.exec_cmd("handy --toggle-transcription")) hl.bind(mainMod .. " + T", hl.dsp.exec_cmd("handy --toggle-transcription"), { release = true }) hl.bind(mainMod .. " + Y", hl.dsp.exec_cmd(handyNo .. " toggle")) hl.bind(mainMod .. " + Y", hl.dsp.exec_cmd(handyNo .. " toggle"), { release = true }) -hl.bind(mainMod .. " + G", hl.dsp.exec_cmd("handy")) -hl.bind(mainMod .. " + SHIFT + G", hl.dsp.exec_cmd(handyNo .. " gui")) +hl.bind(mainMod .. " + G", hl.dsp.exec_cmd("handy")) +hl.bind(mainMod .. " + SHIFT + G", hl.dsp.exec_cmd(handyNo .. " gui")) hl.bind("SUPER + SHIFT + L", hl.dsp.exec_cmd("hyprlock")) -- System shutdown and reboot with confirmation -hl.bind(mainMod .. " + Q", hl.dsp.exec_cmd([=[if [[ $(wofi --dmenu --prompt="Shutdown?" --width=200 --height=100 <<< $'Yes\nNo') == "Yes" ]]; then pkill brave; systemctl poweroff; fi]=])) -hl.bind(mainMod .. " + R", hl.dsp.exec_cmd([=[if [[ $(wofi --dmenu --prompt="Reboot?" --width=200 --height=100 <<< $'Yes\nNo') == "Yes" ]]; then pkill brave; systemctl reboot; fi]=])) +hl.bind(mainMod .. " + Q", + hl.dsp.exec_cmd( + [=[if [[ $(wofi --dmenu --prompt="Shutdown?" --width=200 --height=100 <<< $'Yes\nNo') == "Yes" ]]; then pkill brave; systemctl poweroff; fi]=])) +hl.bind(mainMod .. " + R", + hl.dsp.exec_cmd( + [=[if [[ $(wofi --dmenu --prompt="Reboot?" --width=200 --height=100 <<< $'Yes\nNo') == "Yes" ]]; then pkill brave; systemctl reboot; fi]=])) -- Move focus with mainMod + hjkl hl.bind(mainMod .. " + h", hl.dsp.focus({ direction = "left" })) @@ -256,7 +265,7 @@ hl.bind(mainMod .. " + j", hl.dsp.focus({ direction = "down" })) -- Move active window to a workspace with mainMod + SHIFT + [0-9] for i = 1, 10 do local key = i % 10 -- 10 maps to key 0 - hl.bind(mainMod .. " + " .. key, hl.dsp.focus({ workspace = i })) + hl.bind(mainMod .. " + " .. key, hl.dsp.focus({ workspace = i })) hl.bind(mainMod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i })) end @@ -266,25 +275,29 @@ end -- Scroll through existing workspaces with mainMod + scroll hl.bind(mainMod .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" })) -hl.bind(mainMod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" })) +hl.bind(mainMod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" })) -- Move/resize windows with mainMod + LMB/RMB and dragging -hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true }) +hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true }) hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true }) -- Laptop multimedia keys for volume and LCD brightness -hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"), { locked = true, repeating = true }) -hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { locked = true, repeating = true }) -hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true, repeating = true }) -hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"), { locked = true, repeating = true }) -hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl s 10%+"), { locked = true, repeating = true }) -hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl s 10%-"), { locked = true, repeating = true }) +hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"), + { locked = true, repeating = true }) +hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), + { locked = true, repeating = true }) +hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), + { locked = true, repeating = true }) +hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"), + { locked = true, repeating = true }) +hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl s 10%+"), { locked = true, repeating = true }) +hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl s 10%-"), { locked = true, repeating = true }) -- Requires playerctl -hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true }) +hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true }) hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true }) -hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true }) -hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true }) +hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true }) +hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true }) -------------------------------- diff --git a/scripts/.local/bin/handy-boot-hide b/scripts/.local/bin/handy-boot-hide new file mode 100755 index 0000000..f084f8d --- /dev/null +++ b/scripts/.local/bin/handy-boot-hide @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Handy force-shows its main window at startup whenever the tray icon is +# disabled (lib.rs: "If start_hidden but tray is disabled, we must show +# the window anyway" — it can't know the SUPER+G/SHIFT+G binds are the +# way back in). So --start-hidden alone can't keep the two instances +# hidden at boot. Instead: watch for the first two Handy windows and +# dispatch a close — Handy intercepts close and hides the window. +set -euo pipefail + +hidden=0 +for _ in $(seq 1 150); do # ~30 s window after boot + while read -r addr; do + # Lua-form dispatch: the Lua config provider parses hyprctl + # dispatch args as a Lua expression. + hyprctl dispatch "hl.dsp.window.close({ window = 'address:$addr' })" >/dev/null + hidden=$((hidden + 1)) + done < <(hyprctl clients -j | jq -r '.[] | select(.class == "handy") | .address') + [ "$hidden" -ge 2 ] && exit 0 + sleep 0.2 +done +exit 0