Author SHA1 Message Date
trey 15a20af8a5 Added Ctrl-Alt-Delete menu 2026-06-28 14:42:47 -04:00
trey d014f9c268 Fixed workspace order in single-monitor mode 2026-06-28 13:35:06 -04:00
trey 76226cb637 Fixed randomizeWallpaper() 2026-06-28 11:53:26 -04:00
trey 4f391cf1ca Fixed hypridle 2026-06-28 11:01:17 -04:00
trey 9663c18c7e Updated SUPER + down to cycle to the next window 2026-06-28 09:48:38 -04:00
trey 307163b567 Remapped keybinds to match Windows 11 defaults 2026-06-28 09:45:56 -04:00
trey 3cadcad501 Initial commit after confirming working Lua config 2026-06-27 17:14:39 -04:00
55 changed files with 915 additions and 59 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
}
}
+314
View File
@@ -0,0 +1,314 @@
-- ~/.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"
local black_png = home .. "/images/black.png"
-- Determine if we are on the home WiFi ("keep")
local is_home_wifi = false
local f = io.popen("bash -c '/usr/bin/iwgetid -r 2>/dev/null || /usr/bin/nmcli -t -f active,ssid dev wifi 2>/dev/null | grep \"^yes\" | cut -d: -f2'")
if f then
local ssid = f:read("*a")
f:close()
if ssid and ssid:match("keep") then
is_home_wifi = true
end
end
-- Select image source based on location
local img_dir = nil
if #monitors > 1 then
img_dir = sbm_dir
elseif is_home_wifi then
img_dir = april_dir
end
local images = {}
if img_dir then
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
else
table.insert(images, black_png)
end
if #images == 0 then return end
-- Unload old wallpapers from memory
hl.dispatch(hl.dsp.exec_cmd("hyprctl hyprpaper unload all"))
-- 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 not img then img = images[1] end -- fallback if we run out of unique images
if img then
-- Standard hyprpaper IPC
hl.dispatch(hl.dsp.exec_cmd('hyprctl hyprpaper preload "' .. img .. '"'))
hl.dispatch(hl.dsp.exec_cmd('hyprctl hyprpaper wallpaper "' .. mon.name .. ',' .. img .. '"'))
end
end
end
return M
+5 -9
View File
@@ -1,5 +1,6 @@
general {
lock_cmd = pidof hyprlock || hyprlock # avoid starting multiple hyprlock instances.
unlock_cmd = ~/.local/bin/unlock-ring.sh # run when session is unlocked (hyprlock exits)
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.
}
@@ -17,17 +18,10 @@ listener {
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
# 1. Lock the vault via DBus, 2. Start the lock screen (using ';' so it locks even if the vault fails/is already locked)
on-timeout = busctl --user call org.freedesktop.secrets /org/freedesktop/secrets org.freedesktop.Secret.Service Lock "as" 1 "/org/freedesktop/secrets/aliases/default" ; loginctl lock-session
}
listener {
@@ -40,3 +34,5 @@ listener {
timeout = 900 # 10min
on-timeout = systemd-ac-power || systemctl suspend # only suspend pc on battery
}
# vim:ft=hyprlang
+306
View File
@@ -0,0 +1,306 @@
-- ~/.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("CTRL + ALT + Delete", dsp.exec_cmd("bash ~/.config/hypr/scripts/sysmenu.sh"))
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 + SHIFT + A", function() fn.focusOrLaunch("slack", "Slack", "Slack") end)
bind("SUPER + SHIFT + 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)
-- Windows 11 equivalent shortcuts
bind("SUPER + S", dsp.exec_cmd(launcher)) -- Win+S for Search/Launcher
bind("SUPER + R", dsp.exec_cmd(launcher)) -- Win+R for Run
bind("SUPER + E", dsp.exec_cmd("nautilus")) -- Win+E for Explorer
bind("SUPER + V", dsp.exec_cmd("cliphist list | rofi -dmenu -p 'paste' | cliphist decode | " .. copy .. " && " .. paste)) -- Win+V for Clipboard
bind("SUPER + SHIFT + S", function() fn.focusOrLaunch("pavucontrol", "org.pulseaudio.pavucontrol", "org.pulseaudio.pavucontrol") end)
-- Window control
bind("SUPER + C", dsp.window.close())
bind("ALT + F4", dsp.window.close())
bind("SUPER + T", dsp.window.float({ action = "toggle" }))
bind("SUPER + space", dsp.window.fullscreen({ mode = 1 }))
-- Cycle Layouts
local layouts = { "dwindle", "master", "scrolling" }
local layout_icons = { dwindle = "", master = "", scrolling = "" }
local layout_idx = 1
local function cycle_layout()
layout_idx = (layout_idx % #layouts) + 1
local new_layout = layouts[layout_idx]
hl.config({ general = { layout = new_layout } })
os.execute("echo '" .. layout_icons[new_layout] .. "' > /tmp/hypr_layout.txt")
os.execute("pkill -RTMIN+8 waybar")
end
bind("SUPER + right", function() cycle_layout() end)
bind("SUPER + left", function() cycle_layout() end)
bind("SUPER + up", dsp.window.fullscreen({ mode = 1 }))
bind("SUPER + down", hl.dsp.window.cycle_next())
-- XMonad-style cycle & Alt+Tab (using native dsp dispatchers)
bind("SUPER + J", hl.dsp.window.cycle_next())
bind("SUPER + K", hl.dsp.window.cycle_next({ prev = true }))
bind("ALT + Tab", hl.dsp.window.cycle_next())
bind("ALT + SHIFT + Tab", hl.dsp.window.cycle_next({ prev = true }))
bind("SUPER + X", dsp.exec_cmd("dunstctl close"))
bind("SUPER + Y", dsp.exec_cmd("dunstctl close-all"))
-- Workspace control
bind("SUPER + CTRL + right", function() hl.dispatch(hl.dsp.focus({ workspace = "m+1" })) end)
bind("SUPER + CTRL + 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)
-- 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
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
# Options for the menu
lock="Lock"
logout="Sign-Out"
reboot="Reboot"
poweroff="Power Off"
options="$lock\n$logout\n$reboot\n$poweroff"
# Fullscreen rofi menu with large centered text mimicking Windows 11
chosen=$(echo -e "$options" | rofi -dmenu -i -p "CTRL-ALT-DEL" \
-theme-str 'window { fullscreen: true; background-color: #000000cc; }' \
-theme-str 'mainbox { children: [listview]; padding: 25% 35%; }' \
-theme-str 'listview { columns: 1; lines: 4; spacing: 15px; scrollbar: false; }' \
-theme-str 'element { padding: 15px; border-radius: 10px; }' \
-theme-str 'element-text { horizontal-align: 0.5; vertical-align: 0.5; font: "sans-serif 20"; }' \
-theme-str 'element selected { background-color: #3366ff; text-color: white; }')
case $chosen in
$lock)
loginctl lock-session
;;
$logout)
hyprctl dispatch exit
;;
$reboot)
zsh -c 'source ~/.zsh_functions && reboot'
;;
$poweroff)
systemctl poweroff
;;
esac
+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
+39
View File
@@ -0,0 +1,39 @@
-- ~/.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")
-- Iterate in REVERSE to assign the most negative IDs to the first items, counteracting Waybar's low-to-high sorting!
for i = #ws_config, 1, -1 do
local ws = ws_config[i]
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)