1 Commits
Author SHA1 Message Date
trey 3cadcad501 Initial commit after confirming working Lua config 2026-06-27 17:14:39 -04:00
54 changed files with 825 additions and 50 deletions

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
{
"diagnostics": {
"globals": [
"hl"
]
},
"workspace": {
"library": [
"/usr/share/hypr/stubs",
"/usr/share/hyprland/stubs"
],
"checkThirdParty": false
}
}
+286
View File
@@ -0,0 +1,286 @@
-- ~/.config/hypr/functions.lua
local M = {}
-- focusOrLaunch: checks if a window with the given class exists.
-- If it does, focuses it (and sends specific shortcuts if it's qutebrowser).
-- If not, optionally switches to a workspace and launches the app.
function M.focusOrLaunch(app_cmd, class_name, workspace_name)
local clients = hl.get_windows()
local found = false
for _, client in ipairs(clients) do
if client.class == class_name then
hl.dispatch(hl.dsp.focus({ window = "class:" .. class_name }))
-- Specific qutebrowser logic ported from your zsh script
if app_cmd:match("qutebrowser") then
hl.dispatch(hl.dsp.send_shortcut({ mods = "", key = "Escape", window = "class:" .. class_name }))
hl.dispatch(hl.dsp.send_shortcut({ mods = "", key = "0", window = "class:" .. class_name }))
hl.dispatch(hl.dsp.send_shortcut({ mods = "", key = "1", window = "class:" .. class_name }))
hl.dispatch(hl.dsp.send_shortcut({ mods = "ALT", key = "J", window = "class:" .. class_name }))
hl.dispatch(hl.dsp.send_shortcut({ mods = "", key = "Escape", window = "class:" .. class_name }))
end
found = true
break
end
end
if not found then
if workspace_name and #workspace_name > 0 then
hl.dispatch(hl.dsp.focus({ workspace = workspace_name }))
end
hl.dispatch(hl.dsp.exec_cmd(app_cmd))
end
end
function M.getActiveWorkspaceName()
local f = io.popen("hyprctl -j activeworkspace | jq -r '.name'")
local active_ws_name = f:read("*a"):gsub("%s+", "")
f:close()
return active_ws_name
end
function M.mv2workspace()
local cmd = [[
bash -c '
WORKSPACES=$(hyprctl -j workspaces | jq -r ".[] | .name")
ACTIVE="$(hyprctl -j activewindow)"
CLASS="$(jq -r ".class" <<< "$ACTIVE")"
TITLE="$(jq -r ".title" <<< "$ACTIVE")"
ADDR="$(jq -r ".address" <<< "$ACTIVE")"
PROMPT="Move window of ${CLASS} with title '\''${TITLE}'\'' to which workspace? "
NEW_WS="$(echo "$WORKSPACES" | rofi -dmenu -p "$PROMPT")"
if [ -n "$NEW_WS" ]; then
hyprctl dispatch "hl.dsp.window.move({ workspace = \"$NEW_WS\", window = \"address:$ADDR\", follow = false })"
fi
'
]]
hl.dispatch(hl.dsp.exec_cmd(cmd))
end
function M.createWorkspace(after_name)
local after_str = after_name or ""
local cmd = string.format([[
bash -c '
NEW_NAME=$(rofi -dmenu -p "new workspace name" -l 0 < /dev/null | tr -d "[:space:]")
if [ -z "$NEW_NAME" ]; then exit 0; fi
ACTIVE_MON=$(hyprctl -j monitors | jq -r ".[] | select(.focused) | .name")
NEW_LINE=" { name = \"$NEW_NAME\", monitor = \"$ACTIVE_MON\" },"
FILE="$HOME/.config/hypr/workspace_config.lua"
AFTER="%s"
if [ -n "$AFTER" ] && grep -q "name *= *\"$AFTER\"" "$FILE"; then
sed -i "/name *= *\"$AFTER\"/a \\$NEW_LINE" "$FILE"
else
sed -i "/^}/i \\$NEW_LINE" "$FILE"
fi
hyprctl reload
'
]], after_str)
hl.dispatch(hl.dsp.exec_cmd(cmd))
end
function M.deleteWorkspace()
local cmd = [[
bash -c '
ACTIVE_WS=$(hyprctl -j activeworkspace | jq -r ".name")
if [ -z "$ACTIVE_WS" ]; then exit 0; fi
FILE="$HOME/.config/hypr/workspace_config.lua"
sed -i "/name *= *\"$ACTIVE_WS\"/d" "$FILE"
hyprctl dispatch "hl.dsp.focus({ workspace = \"m-1\" })"
hyprctl reload
'
]]
hl.dispatch(hl.dsp.exec_cmd(cmd))
end
function M.moveWorkspace(direction, target_monitor)
-- If no target_monitor is provided and direction is nil, prompt interactively via async bash to avoid deadlocks
if (not target_monitor or target_monitor == "") and not direction then
local cmd = [[
bash -c '
ACTIVE_WS=$(hyprctl -j activeworkspace | jq -r ".name")
if [ -z "$ACTIVE_WS" ]; then exit 0; fi
MONITORS=$(hyprctl -j monitors | jq -r ".[].name")
PROMPT="Move workspace '\''$ACTIVE_WS'\'' to which monitor? "
NEW_MON=$(echo "$MONITORS" | rofi -dmenu -p "$PROMPT")
if [ -n "$NEW_MON" ]; then
FILE="$HOME/.config/hypr/workspace_config.lua"
sed -i -E "/name *= *\"$ACTIVE_WS\"/ s/monitor *= *\"[^\"]+\"/monitor = \"$NEW_MON\"/" "$FILE"
hyprctl reload
fi
'
]]
hl.dispatch(hl.dsp.exec_cmd(cmd))
return
end
-- NATIVE LUA PATH for directional shifts or explicit monitor arguments passed from keybinds
local active_ws_name = M.getActiveWorkspaceName()
if not active_ws_name or active_ws_name == "" then return end
local path = os.getenv("HOME") .. "/.config/hypr/workspace_config.lua"
local f_in = io.open(path, "r")
if not f_in then return end
local lines = {}
local ws_idx = nil
for line in f_in:lines() do
table.insert(lines, line)
if line:match('name%s*=%s*"' .. active_ws_name .. '"') then
ws_idx = #lines
end
end
f_in:close()
if not ws_idx then return end
if target_monitor and target_monitor ~= "" then
lines[ws_idx] = lines[ws_idx]:gsub('monitor%s*=%s*"[^"]+"', 'monitor = "' .. target_monitor .. '"')
end
if direction then
local first_idx = 2
local last_idx = #lines - 1
while first_idx < #lines and not lines[first_idx]:match("name%s*=") do first_idx = first_idx + 1 end
while last_idx > 1 and not lines[last_idx]:match("name%s*=") do last_idx = last_idx - 1 end
if first_idx <= last_idx then
local current_line = table.remove(lines, ws_idx)
local new_idx = ws_idx
if direction > 0 then
new_idx = ws_idx + 1
if new_idx > last_idx then
new_idx = first_idx
end
elseif direction < 0 then
new_idx = ws_idx - 1
if new_idx < first_idx then
new_idx = last_idx
end
end
table.insert(lines, new_idx, current_line)
end
end
local f_out = io.open(path, "w")
for _, line in ipairs(lines) do
f_out:write(line .. "\n")
end
f_out:close()
hl.dispatch(hl.dsp.exec_cmd("hyprctl reload"))
end
function M.pasteMacro(script_path)
local cmd = string.format([[
bash -c '
exec > /tmp/paste.log 2>&1
out="$(%s)"
echo -n "$out" | wl-copy
# 1. Wait a moment for physical keys to be released
sleep 0.3
# 2. Release modifiers logically, then execute CTRL+V
wtype -m logo -m ctrl -m shift -m alt -M ctrl -k v -m ctrl
# 3. Wait a full second to ensure Wayland apps finish reading the clipboard asynchronously
sleep 1.0
# 4. Scrub the password from cliphist securely
PREV=$(cliphist list | awk "NR==2")
cliphist list | awk "NR==1" | cliphist delete
if [ -n "$PREV" ]; then
echo "$PREV" | cliphist decode | wl-copy
else
wl-copy -c
fi
'
]], script_path)
hl.dispatch(hl.dsp.exec_cmd(cmd))
end
function M.pasteText(text)
local cmd = string.format([[
bash -c '
exec &> /tmp/$(date +%%FT%%H%%M%%S)_paste.log
echo -n "%s" | wl-copy
# Wait a moment for physical keys to be released, then execute CTRL+V
sleep 0.3
wtype -m logo -m ctrl -m shift -m alt -M ctrl -k v -m ctrl
# Note: We do not scrub cliphist here because dates are not sensitive passwords,
# which completely eliminates the paste race condition for dates!
'
]], text)
hl.dispatch(hl.dsp.exec_cmd(cmd))
end
function M.timetracker()
local cmd = [[
bash -c '
CHOICE=$(echo -e "Begin...\nEnd..." | rofi -dmenu -theme-str "inputbar {enabled: false;}" -i -matching prefix -auto-select -l 2)
if [ "$CHOICE" = "Begin..." ]; then
DATE_STR=$(date "+%F %T")
wtype -m logo -m ctrl -m shift -m alt -k Escape
wtype "I${DATE_STR}: Begin "
elif [ "$CHOICE" = "End..." ]; then
DATE_STR=$(date "+%F %T")
wtype -m logo -m ctrl -m shift -m alt -k Escape
wtype "I${DATE_STR}: End "
wtype -k Escape
wtype "kly\$jpo"
wtype "${DATE_STR}: Begin "
fi
'
]]
hl.dispatch(hl.dsp.exec_cmd(cmd))
end
function M.randomizeWallpaper()
local monitors = hl.get_monitors()
local home = os.getenv("HOME")
local april_dir = home .. "/images/April"
local sbm_dir = home .. "/images/Seven Beats Music"
-- Choose image source based on monitor count
local img_dir = (#monitors <= 1) and april_dir or sbm_dir
local images = {}
local handle = io.popen('find "' .. img_dir .. '" -type f \\( -name "*.jpg" -o -name "*.png" -o -name "*.jpeg" \\) 2>/dev/null')
if handle then
for line in handle:lines() do
table.insert(images, line)
end
handle:close()
end
if #images == 0 then return end
-- Pick a unique random image for each monitor
math.randomseed(os.time())
local used = {}
for _, mon in ipairs(monitors) do
local img = nil
local attempts = 0
while attempts < #images do
local idx = math.random(1, #images)
if not used[idx] then
img = images[idx]
used[idx] = true
break
end
attempts = attempts + 1
end
if img then
hl.dispatch(hl.dsp.exec_cmd('hyprctl hyprpaper reload "' .. mon.name .. ',' .. img .. '"'))
end
end
end
return M
+2
View File
@@ -40,3 +40,5 @@ listener {
timeout = 900 # 10min
on-timeout = systemd-ac-power || systemctl suspend # only suspend pc on battery
}
# vim:ft=hyprlang
+282
View File
@@ -0,0 +1,282 @@
-- ~/.config/hypr/hyprland.luaA
-----------------------------------------------------
-- Variables (from variables.conf)
-----------------------------------------------------
local copy = "wl-copy --trim-newline"
local paste = "wl-paste"
local launcher = "rofi -show run -p 'launch'"
-- local clear = "cliphist list | head -n1 | cliphist delete"
-----------------------------------------------------
-- Monitors (from monitors.conf)
-----------------------------------------------------
hl.monitor({ output = "desc:Ancor Communications Inc ASUS VS228 E3LMTF071438", mode = "preferred", position = "0x0", scale = "1" }) -- DP-7, left
hl.monitor({ output = "desc:Samsung Display Corp. 0x4193", mode = "preferred", position = "1920x0", scale = "2" }) -- eDP-1, center
hl.monitor({ output = "desc:Ancor Communications Inc ASUS VS228 E3LMTF071391", mode = "preferred", position = "3360x0", scale = "1" }) -- DP-8, right
hl.monitor({ output = "desc:Stargate Technology FHD", mode = "preferred", position = "auto", scale = "auto" }) -- portable monitor
-----------------------------------------------------
-- Environment Variables (from env.conf)
-----------------------------------------------------
hl.env("GDK_BACKEND", "wayland,x11,*")
hl.env("GUI_ENABLE_WAYLAND", "1")
hl.env("HYPRCURSOR_SIZE", "18")
hl.env("QT_QPA_PLATFORM", "wayland;xcb")
hl.env("QT_QPA_PLATFORMTHEME", "qt6ct")
hl.env("QT_AUTO_SCREEN_SCALE_FACTOR", "1")
hl.env("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1")
hl.env("XCURSOR_SIZE", "18")
hl.env("XDG_CURRENT_DESKTOP", "Hyprland")
hl.env("XDG_SESSION_TYPE", "wayland")
hl.env("XDG_SESSION_DESKTOP", "Hyprland")
-----------------------------------------------------
-- Appearance & Input (from appearance.conf & input.conf)
-----------------------------------------------------
hl.config({
general = {
gaps_in = 1,
gaps_out = 2,
border_size = 2,
col = {
active_border = "rgba(fabd2f99)",
inactive_border = "rgba(595959aa)",
},
resize_on_border = false,
allow_tearing = false,
layout = "dwindle"
},
decoration = {
rounding = 10,
rounding_power = 2,
active_opacity = 1.0,
inactive_opacity = 1.0,
shadow = {
enabled = true,
range = 4,
render_power = 3,
color = "rgba(1a1a1aee)"
},
blur = {
enabled = true,
size = 3,
passes = 1,
vibrancy = 0.1696
}
},
animations = {
enabled = true,
bezier = {
"easeOutQuint,0.23,1,0.32,1",
"easeInOutCubic,0.65,0.05,0.36,1",
"linear,0,0,1,1",
"almostLinear,0.5,0.5,0.75,1.0",
"quick,0.15,0,0.1,1"
},
animation = {
"global, 1, 10, default",
"border, 1, 5.39, easeOutQuint",
"windows, 1, 4.79, easeOutQuint",
"windowsIn, 1, 4.1, easeOutQuint, popin 87%",
"windowsOut, 1, 1.49, linear, popin 87%",
"fadeIn, 1, 1.73, almostLinear",
"fadeOut, 1, 1.46, almostLinear",
"fade, 1, 3.03, quick",
"layers, 1, 3.81, easeOutQuint",
"layersIn, 1, 4, easeOutQuint, fade",
"layersOut, 1, 1.5, linear, fade",
"fadeLayersIn, 1, 1.79, almostLinear",
"fadeLayersOut, 1, 1.39, almostLinear",
"workspaces, 1, 1.94, almostLinear, fade",
"workspacesIn, 1, 1.21, almostLinear, fade",
"workspacesOut, 1, 1.94, almostLinear, fade"
}
},
dwindle = { preserve_split = true },
master = { new_status = "master" },
misc = {
on_focus_under_fullscreen = 1,
force_default_wallpaper = 0,
disable_hyprland_logo = true
},
input = {
resolve_binds_by_sym = 1,
kb_layout = "us, us",
kb_variant = "colemak,",
kb_options = "ctrl:nocaps, compose:ralt, grp:ctrls_toggle",
follow_mouse = 1,
sensitivity = 0,
touchpad = { natural_scroll = false }
}})
hl.device({
name = "epic-mouse-v1",
sensitivity = -0.5
})
-----------------------------------------------------
-- Window Rules (from windowrules.conf)
-----------------------------------------------------
-- XWayland dragging
hl.window_rule({
no_focus = true,
match = { xwayland = true, float = true, fullscreen = false, pin = false }
})
-- Floating rules
local float_class_regex = "(gcr-prompter|qt5ct|qt6ct|simple-scan|espanso|nm-applet)"
local float_title_regex = "(\"HP Device Manager - Plug-in Installer\")"
local float_regex = "(" .. float_class_regex .. "|" .. float_title_regex .. ")"
hl.window_rule({ float = true, match = { class = float_class_regex } })
hl.window_rule({ float = true, match = { title = float_title_regex } })
-- Pin rules
local pin_regex = "(gcr-prompter|nm-applet)"
hl.window_rule({ pin = true, match = { class = pin_regex } })
-- Maximize everything else (fixed typo fioat_regex -> float_regex)
hl.window_rule({
maximize = true,
match = { class = "negative:" .. float_regex }
})
-- Espanso on special
hl.window_rule({
workspace = "special",
match = { title = "Espanso Sync Tool" }
})
-- App-specific workspaces
local browsers = "(.*qutebrowser.*|.*[Ff]irefox.*)"
local meeting = "(.*[Zz]oom.*)"
hl.window_rule({ workspace = "name:shell", match = { class = ".*[Aa]lacritty.*" } })
hl.window_rule({ workspace = "name:browser silent", match = { class = browsers } })
hl.window_rule({ workspace = "name:jobs silent", match = { class = ".*[Vv]ivaldi-stable.*" } })
hl.window_rule({ workspace = "name:meeting silent", match = { class = meeting } })
-----------------------------------------------------
-- Workspaces (from workspaces.conf)
-----------------------------------------------------
require("workspaces")
-----------------------------------------------------
-- Keybindings & Macros (from keybindings.conf & macros.conf)
-----------------------------------------------------
local bind = hl.bind
local dsp = hl.dsp
local fn = require("functions")
bind("SUPER + Q", dsp.exec_cmd("hyprctl reload && killall -SIGUSR2 waybar && notify-send 'Hyprland' 'Config Reloaded' -u low -t 2000"))
-- BUG FIX: SUPER + SHIFT + Q was bound to both tmux kill and uwsm stop.
-- bind("SUPER + SHIFT + Q", dsp.exec_cmd("tmux kill-server"))
bind("SUPER + SHIFT + Q", dsp.exec_cmd("killall -q signal-desktop; uwsm stop"))
bind("SUPER + L", dsp.exec_cmd("hyprlock"))
bind("switch:Lid Switch", dsp.exec_cmd("hyprlock"))
-- Screenshots (evaluated natively in Lua at keypress time)
bind("CTRL + SHIFT + 3", function()
local cmd = string.format("grim 'images/screenshots/%s_screenshot.png' && paplay ~/wav/camera.wav", os.date("%FT%H%M%S"))
hl.dispatch(hl.dsp.exec_cmd(cmd))
end)
bind("CTRL + SHIFT + 4", function()
local cmd = string.format("slurp | grim -g - '%s_screenshot.png' && paplay ~/wav/camera.wav", os.date("%FT%H%M%S"))
hl.dispatch(hl.dsp.exec_cmd(cmd))
end)
-- App launchers using our custom Lua focusOrLaunch function
bind("SUPER + return", function() fn.focusOrLaunch("alacritty", "Alacritty") end)
bind("SUPER + A", function() fn.focusOrLaunch("slack", "Slack", "Slack") end)
bind("SUPER + D", function() fn.focusOrLaunch("gnome-dictionary", "org.gnome.Dictionary", "org.gnome.Dictionary") end)
bind("SUPER + G", function() fn.focusOrLaunch("vivaldi-stable", "vivaldi-stable", "vivaldi-stable") end)
bind("SUPER + ALT + H", function() fn.focusOrLaunch("qutebrowser", "org.qutebrowser.qutebrowser") end)
bind("SUPER + P", dsp.exec_cmd(launcher))
bind("SUPER + S", function() fn.focusOrLaunch("pavucontrol", "org.pulseaudio.pavucontrol", "org.pulseaudio.pavucontrol") end)
bind("SUPER + V", function() fn.focusOrLaunch("alacritty", "Alacritty") end)
-- Window control
bind("SUPER + C", dsp.window.close())
bind("SUPER + T", dsp.window.float({ action = "toggle" }))
bind("SUPER + space", dsp.window.fullscreen({ mode = 1 }))
-- BUG FIX: H/J/K were redundantly bound to move_focus AND fullscreen.
bind("SUPER + H", dsp.focus({ direction = "l" }))
bind("SUPER + K", dsp.focus({ direction = "u" }))
bind("SUPER + J", dsp.focus({ direction = "d" }))
bind("SUPER + X", dsp.exec_cmd("dunstctl close"))
bind("SUPER + Y", dsp.exec_cmd("dunstctl close-all"))
-- Workspace control
bind("SUPER + right", function() hl.dispatch(hl.dsp.focus({ workspace = "m+1" })) end)
bind("SUPER + left", function() hl.dispatch(hl.dsp.focus({ workspace = "m-1" })) end)
bind("SUPER + SHIFT + H", function() hl.dispatch(hl.dsp.focus({ workspace = "m-1" })) end)
bind("SUPER + SHIFT + L", function() hl.dispatch(hl.dsp.focus({ workspace = "m+1" })) end)
bind("SUPER + SHIFT + M", function() fn.mv2workspace() end)
bind("SUPER + SHIFT + N", function() fn.createWorkspace() end)
bind("SUPER + SHIFT + X", function() fn.deleteWorkspace() end)
bind("SUPER + SHIFT + Z", function() fn.createWorkspace(fn.getActiveWorkspaceName()) end)
bind("SUPER + SHIFT + left", function() fn.moveWorkspace(-1) end)
bind("SUPER + SHIFT + right", function() fn.moveWorkspace(1) end)
bind("SUPER + ALT + DOWN", function() fn.moveWorkspace(nil, "DP-8") end)
-- Cliphist
bind("ALT + V", dsp.exec_cmd("cliphist list | rofi -dmenu -p 'paste' | cliphist decode | " .. copy .. " && " .. paste))
-- Laptop keys
bind("XF86MonBrightnessUp", dsp.exec_cmd("brightnessctl -- set +10%"))
bind("XF86MonBrightnessDown", dsp.exec_cmd("brightnessctl -- set -10%"))
-- Macros (uses fn.pasteText and native Lua os.time/os.date instead of bash scripts)
bind("SUPER + CTRL + S", function() fn.pasteText(os.date("%F")) end)
bind("SUPER + CTRL + N", function() fn.pasteText(os.date("%FT%T %z")) end)
bind("SUPER + CTRL + Y", function() fn.pasteText(os.date("%F", os.time() - 86400)) end)
bind("SUPER + CTRL + T", function() fn.pasteText(os.date("%F", os.time() + 86400)) end)
-- Timetracker Macro
bind("CTRL + SHIFT + S", function() fn.timetracker() end)
-- Bitwarden Macro
bind("SUPER + CTRL + 1", function() fn.pasteMacro("secret-tool lookup app bw") end)
-----------------------------------------------------
-- Autostart (from autostart.conf)
-----------------------------------------------------
hl.on("hyprland.start", function()
hl.exec_cmd("dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP")
hl.exec_cmd("hyprpolkitagent")
hl.exec_cmd("uwsm app -- hypridle")
hl.exec_cmd("uwsm app -- hyprpaper")
hl.exec_cmd("uwsm app -- waybar")
hl.exec_cmd("uwsm app -- espanso start")
hl.exec_cmd("uwsm app -- dunst")
hl.exec_cmd("nm-applet")
hl.exec_cmd("signal-desktop")
hl.exec_cmd("hp-systray")
hl.exec_cmd("blueman-applet")
hl.exec_cmd("wl-paste --watch cliphist store")
hl.exec_cmd("wl-clip-persist --clipboard regular")
hl.exec_cmd("qutebrowser")
hl.exec_cmd("vivaldi-stable")
hl.exec_cmd("alacritty")
-- Start the battery low-level notifier
hl.exec_cmd("bash ~/.config/hypr/scripts/battery-notify.sh")
-- Randomize wallpapers after hyprpaper has time to start
hl.exec_cmd("sleep 2 && hyprctl eval 'require(\"functions\").randomizeWallpaper()'")
end)
+8 -8
View File
@@ -1,19 +1,20 @@
ipc = true
splash = false
wallpaper {
monitor = DP-7
path = /home/trey/images/black.png
fit_mode = cover
}
wallpaper {
monitor = eDP-1
path = /home/trey/images/black.png
fit_mode = cover
}
wallpaper {
monitor = DP-7
path = /home/trey/images/Seven Beats Music/2024-09-16.jpg
fit_mode = cover
}
wallpaper {
monitor = DP-8
path = /home/trey/images/Seven Beats Music/2024-10-14.jpg
path = /home/trey/images/black.png
fit_mode = cover
}
@@ -22,4 +23,3 @@ wallpaper {
path = /home/trey/images/black.png
fit_mode = cover
}
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
+42
View File
@@ -0,0 +1,42 @@
general {
lock_cmd = pidof hyprlock || hyprlock # avoid starting multiple hyprlock instances.
before_sleep_cmd = loginctl lock-session # lock before suspend.
after_sleep_cmd = hyprctl dispatch dpms on # to avoid having to press a key twice to turn on the display.
}
listener {
timeout = 150 # 2.5min.
on-timeout = brightnessctl -s set 10 # set monitor backlight to minimum, avoid 0 on OLED monitor.
on-resume = brightnessctl -r # monitor backlight restore.
}
# turn off keyboard backlight, comment out this section if you dont have a keyboard backlight.
listener {
timeout = 150 # 2.5min.
on-timeout = brightnessctl -sd rgb:kbd_backlight set 0 # turn off keyboard backlight.
on-resume = brightnessctl -rd rgb:kbd_backlight # turn on keyboard backlight.
}
#listener {
# timeout = 300 # 5min
# on-timeout = loginctl lock-session # lock screen when timeout has passed
#}
listener {
timeout = 300
# 1. Lock the vault via DBus, 2. Start the lock screen
on-timeout = busctl --user call org.freedesktop.secrets /org/freedesktop/secrets org.freedesktop.Secret.Service Lock "as" 1 "/org/freedesktop/secrets/aliases/default" && hyprlock
# When hyprlock exits (via fingerprint or password), instantly run the unlock script
on-resume = ~/.local/bin/unlock-ring.sh
}
listener {
timeout = 330 # 5.5min
on-timeout = hyprctl dispatch dpms off # screen off when timeout has passed
on-resume = hyprctl dispatch dpms on # screen on when activity is detected after timeout has fired.
}
listener {
timeout = 900 # 10min
on-timeout = systemd-ac-power || systemctl suspend # only suspend pc on battery
}
File renamed without changes.
+70
View File
@@ -0,0 +1,70 @@
auth {
fingerprint {
enabled = true
}
}
background {
# path = screenshot
color = rgb(000000)
}
input-field {
size = 200, 30
outline_thickness = 1
rounding = 10
outer_color = rgb(665c54)
inner_color = rgb(3c3836)
font_color = rgb(ebdbb2)
check_color = rgb(d79921)
fail_color = rgb(cc241d)
fade_on_empty = false
placeholder_text = password
fail_text =
}
label {
text = cmd[update:500]date +"%FT%H:%M:%S"
color = rgb(ebdbb2)
font_size = 45
font_family = monospace
position = 0, 60
halign = center
valign = center
}
label {
text = cmd[update:3000]pidof spotify > /dev/null && echo "<span foreground='##b8bb26' size='25000' rise='-4.5pt'> </span>$(playerctl -p spotify metadata xesam:title) - $(playerctl -p spotify metadata xesam:artist)"
color = rgb(ebdbb2)
font_size = 14
font_family = JetBrains Mono Nerd Font Mono Semibold
position = 0, -420
halign = center
valign = center
}
label {
text = cmd[update:60] uptime -p
color = rgb(ebdbb2)
offont_size = 10
font_family = monospace
position = 0, -450
halign = center
valign = center
}
listener {
timeout = 300 # 5 minutes
on-timeout = sleep 60 && busctl --user call org.freedesktop.secrets /org/freedesktop/secrets org.freedesktop.Secret.Service Lock "as" 1 "/org/freedesktop/secrets/aliases/default" && hyprlock
}
listener {
timeout = 330 # 5.5 minutes
on-timeout = hyprctl dispatch dpms off # screen off
on-resume = hyprctl dispatch dpms on # screen on
}
# vim:ft=hyprlang
+25
View File
@@ -0,0 +1,25 @@
splash = false
wallpaper {
monitor = eDP-1
path = /home/trey/images/black.png
fit_mode = cover
}
wallpaper {
monitor = DP-7
path = /home/trey/images/Seven Beats Music/2024-09-16.jpg
fit_mode = cover
}
wallpaper {
monitor = DP-8
path = /home/trey/images/Seven Beats Music/2024-10-14.jpg
fit_mode = cover
}
wallpaper {
monitor =
path = /home/trey/images/black.png
fit_mode = cover
}
View File
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+7
View File
@@ -0,0 +1,7 @@
workspace = 1, defaultName:shell, monitor:eDP-1, persistent:true, default:true
workspace = 3, defaultName:browser, monitor:eDP-1, persistent:true, default:true
workspace = 4, defaultName:work, monitor:eDP-1, persistent:true, default:true
workspace = 5, defaultName:jobs, monitor:eDP-1, persistent:true, default:true
workspace = 6, defaultName:meeting, monitor:eDP-1, persistent:true, default:true
workspace = 7, defaultName:monitoring, monitor:eDP-1, persistent:true, default:true
workspace = 8, defaultName:ai, monitor:eDP-1, persistent:true, default:true
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# Background loop to monitor battery status and trigger dunstify on low power.
while true; do
if [ ! -d /sys/class/power_supply/BAT0 ]; then
sleep 60
continue
fi
STATUS=$(cat /sys/class/power_supply/BAT0/status 2>/dev/null)
# Using the native kernel capacity file instead of relying on ibam
CAPACITY=$(cat /sys/class/power_supply/BAT0/capacity 2>/dev/null)
if [ -z "$CAPACITY" ]; then
sleep 60
continue
fi
# Trigger notification at 5% or below
if [ "$STATUS" == "Discharging" ] && [ "$CAPACITY" -le 5 ]; then
if [ ! -f "/run/user/${UID}/battery_low" ]; then
logger "Low power! Triggering notification...."
dunstify --urgency=critical \
--icon=/usr/share/icons/breeze-dark/emblems/16/emblem-warning.svg \
"LOW BATTERY!" \
"Battery level is at ${CAPACITY}%, plug power quick!"
touch "/run/user/${UID}/battery_low"
fi
# Clear the notification if we plug back in and rise above 5%
elif [ "$STATUS" == "Charging" ] && [ "$CAPACITY" -gt 5 ]; then
if [ -f "/run/user/${UID}/battery_low" ]; then
logger "Power reconnected and above 5 percent, clearing notification state."
rm -f "/run/user/${UID}/battery_low"
# Find and close the specific dunst notification by ID
for id in $(dunstctl history | jq '.data[0][] | select(.summary.data == "LOW BATTERY!") | .id.data'); do
dunstify --close=${id}
done
fi
fi
sleep 60
done
-35
View File
@@ -1,35 +0,0 @@
#!/usr/bin/env zsh
#set -x
old_profile="$(kanshictl status | jq -r '.current_profile')"
new_profile="${1}"
shift
if [[ "${old_profile}" == "${new_profile}" ]]; then
if [[ "${1}" != "--force" ]] && [[ "${1}" != '-f' ]]; then
exit 0
fi
fi
case "${new_profile}" in
home_office)
perl -pi -e 'if (/shell/) {s/monitor:.*?,/monitor:DP-6,/}' ${XDG_CONFIG_HOME}/hypr/workspaces.conf
perl -pi -e 'if (/browser/) {s/monitor:.*?,/monitor:DP-6,/}' ${XDG_CONFIG_HOME}/hypr/workspaces.conf
perl -pi -e 'if (/work/) {s/monitor:.*?,/monitor:HDMI-A-1,/}' ${XDG_CONFIG_HOME}/hypr/workspaces.conf
perl -pi -e 'if (/jobs/) {s/monitor:.*?,/monitor:HDMI-A-1,/}' ${XDG_CONFIG_HOME}/hypr/workspaces.conf
kanshictl switch home_office
hyprctl reload
killall -s USR2 waybar
;;
single_headed)
perl -pi -e 's/monitor:.*?,/monitor:eDP-1,/' ${XDG_CONFIG_HOME}/hypr/workspaces.conf
kanshictl switch single_headed
hyprctl reload
killall -s USR2 waybar
;;
*)
print "Invalid profile!" >&2
;;
esac
#set +x
+8
View File
@@ -0,0 +1,8 @@
return {
{ name = "shell", monitor = "DP-8" },
{ name = "browser", monitor = "DP-8" },
{ name = "jobs", monitor = "DP-7" },
{ name = "work", monitor = "DP-7" },
{ name = "meeting", monitor = "eDP-1" },
{ name = "monitoring", monitor = "eDP-1" },
}
-7
View File
@@ -1,7 +0,0 @@
workspace = 1, defaultName:shell, monitor:DP-8, persistent:true, default:true
workspace = 3, defaultName:browser, monitor:DP-8, persistent:true, default:true
workspace = 4, defaultName:work, monitor:DP-7, persistent:true, default:true
workspace = 5, defaultName:jobs, monitor:DP-7, persistent:true, default:true
workspace = 6, defaultName:meeting, monitor:eDP-1, persistent:true, default:true
workspace = 7, defaultName:monitoring, monitor:eDP-1, persistent:true, default:true
workspace = 8, defaultName:ai, monitor:eDP-1, persistent:true, default:true
+37
View File
@@ -0,0 +1,37 @@
-- ~/.config/hypr/workspaces.lua
-- Dynamic workspace assignment: replaces kanshi + scripts
-- Detects monitor configuration and assigns workspaces accordingly.
local function get_monitor_names()
local names = {}
for _, m in ipairs(hl.get_monitors()) do
names[m.name] = true
end
return names
end
local function assign_workspaces()
local monitors = get_monitor_names()
local has_dock = monitors["DP-7"] and monitors["DP-8"]
-- Read workspace configurations
package.loaded["workspace_config"] = nil -- force reload in case it changed
local ws_config = require("workspace_config")
for _, ws in ipairs(ws_config) do
local target_mon = has_dock and ws.monitor or "eDP-1"
hl.workspace_rule({ workspace = "name:" .. ws.name, monitor = target_mon, persistent = true, default = true })
end
end
-- Apply on initial load
assign_workspaces()
-- Re-apply when monitors change
hl.on("monitor.added", function(monitor)
assign_workspaces()
end)
hl.on("monitor.removed", function(monitor)
assign_workspaces()
end)