Compare commits

...

8 commits

Author SHA1 Message Date
Jeena
997f112baa fix: drop upstream multi-GPU workarounds that don't work on Chester
Restore hypridle.conf to the working Chester baseline (Step 1 dim + simple
step-3 resume) keeping our after_sleep_cmd (sleep 2; dpms on). Remove the
orphaned wake-display.sh / unstick-display.sh scripts added upstream for the
multi-GPU black-screen workaround.
2026-08-24 10:18:08 +09:00
Jeena
d0cfa59fb0 chore(hosts): drop redundant return {} from host lua files
require() only needs the module's side effects; the returned value is
ignored by hyprland.lua. Chester.lua and example.lua now match
Rutherford.lua in ending after their config statements.
2026-08-24 10:15:06 +09:00
Jeena
6365e1d122 refactor(hosts): never load example.lua; make it a pure template
hyprland.lua now just uses its defaults when no hosts/<hostname>.lua
matches, instead of requiring hosts/example.lua. example.lua is kept only
as a documented template for contributors.
2026-08-24 10:15:06 +09:00
Jeena
70807f473a chore: track all hypr/hosts files (remove host gitignore)
Let reusable host configs be versioned regardless of suffix, so others
don't lose history and their own host files get committed.
2026-08-24 10:15:06 +09:00
Jeena
e9f65e1675 chore(hypr): remove legacy .conf configs now that Lua is active
Hyprland loads hyprland.lua (not hyprland.conf) when present, so the
hyprlang configs are dead weight. Delete the Hyprland .conf files and
their gitignored host .conf counterparts, leaving only hypridle.conf and
hyprlock.conf (used by separate tools that still use hyprlang).
2026-08-24 10:15:06 +09:00
Jeena
2e6e19348a fix(hosts): set Chester display scaling to 1.5
Keeps the 2880x1800 internal panel at a comfortable virtual 1920x1200.
2026-08-24 10:11:22 +09:00
Jeena
b4fa650685 feat: convert hypr config to Lua and add Chester host
- Add Lua equivalents for hyprland, autostart, keybindings, programs, windowrule
- Add per-host Lua configs (Chester, Rutherford, example) with hostname detection
- Rename old host 'William' to 'Chester'; fix 2x scaling on the 2880x1800 panel
- Update .gitignore to track *.lua host files instead of example-host.conf
2026-08-24 10:11:22 +09:00
Jeena
00d0cac9e8 fix: black screen on wake from suspend (hypridle)
Enable misc:key_press_enables_dpms / mouse_move_enables_dpms so the
compositor re-enables the display on input when hypridle's after-sleep
dpms-on races with GPU resume. Also delay after_sleep_cmd dpms-on by 2s.

Fixes black Hyprland VT after wake while kernel console stays visible.
2026-08-24 10:11:22 +09:00
17 changed files with 431 additions and 487 deletions

2
.gitignore vendored
View file

@ -1,3 +1 @@
hypr/menu-*.list
hypr/hosts/*
!hpyr/hosts/example-host.conf

View file

@ -1,11 +0,0 @@
exec-once = fcitx5
exec-once = ~/.config/hypr/scripts/wob-daemon.sh
exec-once = [workspace 1 silent] $browser
exec-once = [workspace 2 silent] thunderbird
exec-once = [workspace 2 silent] element-desktop
exec-once = [workspace special:magic silent] $terminal
# Qt apps don't work with workspace on exec-one
# Added windowrule for them
exec-once = keepassxc
exec-once = chromium --profile-directory="Default"

16
hypr/autostart.lua Normal file
View file

@ -0,0 +1,16 @@
-- Autostart (converted from autostart.conf)
local P = require("programs")
-- Qt apps don't work with workspace on exec-once
-- Added windowrule for them
hl.on("hyprland.start", function()
hl.exec_cmd("fcitx5")
hl.exec_cmd("~/.config/hypr/scripts/wob-daemon.sh")
hl.exec_cmd("[workspace 1 silent] " .. P.browser)
hl.exec_cmd("[workspace 2 silent] thunderbird")
hl.exec_cmd("[workspace 2 silent] element-desktop")
hl.exec_cmd("[workspace special:magic silent] " .. P.terminal)
hl.exec_cmd("keepassxc")
hl.exec_cmd('chromium --profile-directory="Default"')
end)

11
hypr/hosts/Chester.lua Normal file
View file

@ -0,0 +1,11 @@
-- Host-specific overrides: Chester (laptop, 2880x1800 internal display)
-- Loaded automatically by hyprland.lua via hostname detection.
-- Keeps the internal panel at 1.5x scaling (virtual 1920x1200).
-- Laptop internal display — explicit & stable
hl.monitor({
output = "eDP-1",
mode = "2880x1800@60",
position = "0x0",
scale = 1.5,
})

View file

@ -0,0 +1,9 @@
-- Host-specific overrides: Rutherford (desktop PC)
-- Loaded automatically by hyprland.lua via hostname detection.
hl.monitor({
output = "DP-1",
mode = "2560x1440@74.97",
position = "0x0",
scale = 1,
})

8
hypr/hosts/example.lua Normal file
View file

@ -0,0 +1,8 @@
-- example.lua — TEMPLATE for a per-host config file (NOT loaded at runtime).
--
-- Hyprland's Lua config only loads hosts/<hostname>.lua matching the current
-- machine's hostname. If none matches, hyprland.lua simply uses its defaults.
-- This file is never required; it exists purely as a documented template.
--
-- To add your own machine, copy this to hosts/<your-hostname>.lua and fill it
-- in. The hostname lookup strips any domain suffix (e.g. "laptop.home" -> "laptop").

View file

@ -2,13 +2,21 @@ general {
inhibit_sleep = 3
lock_cmd = pidof hyprlock || ~/.config/hypr/scripts/lock.sh
before_sleep_cmd = loginctl lock-session
# Cycle dpms off/on after resume: forces Hyprland to recreate the hyprlock
# surfaces and re-grant them keyboard focus, which is otherwise lost after
# suspend (hyprlock shows the clock but ignores typing until a dpms cycle)
after_sleep_cmd = hyprctl dispatch dpms off && sleep 1 && hyprctl dispatch dpms on
# Small delay so the DRM/GPU is ready before re-enabling the display;
# running `dpms on` immediately at resume is the classic cause of a black screen.
after_sleep_cmd = sh -c 'sleep 2; hyprctl dispatch dpms on'
}
# Step 1: Lock screen at 2 min idle
# Step 1: Dim monitor at 115 sec idle (warning that it will lock soon)
listener {
timeout = 115
# Only dims the screen, does not lock yet
# This shows you that lock will happen soon
on-timeout = dim
on-resume = pkill -x dim
}
# Step 2: Lock screen at 2 min idle
listener {
timeout = 120
on-timeout = ~/.config/hypr/scripts/lock.sh
@ -27,19 +35,13 @@ listener {
# Both together ensure the screen is fully off visually and electrically
# When the user becomes active again, restore display
#
# Cycles DPMS only if the display was actually turned off (avoids flicker
# when the user returns before step 3 fires). Needed because otherwise
# hyprlock surfaces may lose focus after a dpms off cycle
# (same issue covered in after_sleep_cmd above).
on-resume = ~/.config/hypr/scripts/wake-display.sh
on-resume = hyprctl dispatch dpms on && ddcutil setvcp 10 100 && wpctl set-mute @DEFAULT_AUDIO_SINK@ 0
# Explanation:
# - wake-display.sh checks if dpms is off; only then does a dpms off/on
# cycle to force Hyprland to recreate hyprlock surfaces
# - hyprctl dispatch dpms on → ensures the display is powered on by the compositor
# - ddcutil setvcp 10 100 → restores monitor brightness to full
#
# Both together guarantee the screen wakes reliably
# Both together guarantee the screen wakes reliably on all monitors
}
# Step 4: Suspend system after 30 min idle

View file

@ -1,331 +0,0 @@
# https://wiki.hyprland.org/Configuring/
# You can split this configuration into multiple files
# Create your files separately and then link them to this file like this:
# source = ~/.config/hypr/myColors.conf
#############################
### ENVIRONMENT VARIABLES ###
#############################
# See https://wiki.hyprland.org/Configuring/Environment-variables/
env = XCURSOR_SIZE,24
env = HYPRCURSOR_SIZE,24
env = QT_QPA_PLATFORMTHEME,qt5ct
env = HYPRSHOT_DIR,Screenshots
# Force Aquamarine to use only the Intel iGPU for display.
# The NVIDIA RTX 3060 is also present but causes EGL_BAD_MATCH errors and page-flip
# lockups after VT/DPMS cycles (multi-GPU buffer ownership fights).
env = AQ_DRM_DEVICES,/dev/dri/card0
################
### MONITORS ###
################
# See https://wiki.hyprland.org/Configuring/Monitors/
# Safe default for any machine
monitor = ,preferred,auto,1
###################
### MY PROGRAMS ###
###################
# See https://wiki.hyprland.org/Configuring/Keywords/
# Set programs that you use
source = ~/.config/hypr/programs.conf
#################
### AUTOSTART ###
#################
# Autostart necessary processes (like notifications daemons, status bars, etc.)
# Or execute your favorite apps at launch like this:
# exec-once = hypridle # DISABLED: auto-lock causes black screen (multi-GPU + orphaned layer issues).
# TODO: re-enable once root cause is fixed
exec-once = waybar
exec-once = swaybg -i ~/.config/hypr/wallpapers/Purple.jpg -m fill
exec-once = ~/.config/hypr/scripts/dynamic-borders.sh
exec-once = ~/.config/hypr/scripts/xdg-desktop-portal-hyprland.sh
source = ~/.config/hypr/autostart.conf
###################
### PERMISSIONS ###
###################
# See https://wiki.hyprland.org/Configuring/Permissions/
# Please note permission changes here require a Hyprland restart and are not applied on-the-fly
# for security reasons
# ecosystem {
# enforce_permissions = 1
# }
# permission = /usr/(bin|local/bin)/grim, screencopy, allow
# permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow
# permission = /usr/(bin|local/bin)/hyprpm, plugin, allow
#####################
### LOOK AND FEEL ###
#####################
# Refer to https://wiki.hyprland.org/Configuring/Variables/
# https://wiki.hyprland.org/Configuring/Variables/#general
general {
gaps_in = 5
gaps_out = 1,0,0,0
border_size = 3
# https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors
col.active_border = rgba(D55D7Cee) rgba(480023ee) 45deg
col.inactive_border = rgba(595959aa)
# Set to true enable resizing windows by clicking and dragging on borders and gaps
resize_on_border = true
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false
layout = dwindle
}
# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration {
rounding = 10
rounding_power = 2
# Change transparency of focused and unfocused windows
active_opacity = 1.0
inactive_opacity = 0.8
shadow {
enabled = true
range = 4
render_power = 3
color = rgba(1a1a1aee)
}
# https://wiki.hyprland.org/Configuring/Variables/#blur
blur {
enabled = true
size = 3
passes = 1
vibrancy = 0.1696
}
}
# https://wiki.hyprland.org/Configuring/Variables/#animations
animations {
enabled = yes, please :)
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
bezier = easeOutQuint,0.23,1,0.32,1
bezier = easeInOutCubic,0.65,0.05,0.36,1
bezier = linear,0,0,1,1
bezier = almostLinear,0.5,0.5,0.75,1.0
bezier = quick,0.15,0,0.1,1
animation = global, 1, 10, default
animation = border, 1, 5.39, easeOutQuint
animation = windows, 1, 4.79, easeOutQuint
animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
animation = windowsOut, 1, 1.49, linear, popin 87%
animation = fadeIn, 1, 1.73, almostLinear
animation = fadeOut, 1, 1.46, almostLinear
animation = fade, 1, 3.03, quick
animation = layers, 1, 3.81, easeOutQuint
animation = layersIn, 1, 4, easeOutQuint, fade
animation = layersOut, 1, 1.5, linear, fade
animation = fadeLayersIn, 1, 1.79, almostLinear
animation = fadeLayersOut, 1, 1.39, almostLinear
animation = workspaces, 1, 1.94, easeOutQuint, slidefade
animation = workspacesIn, 1, 1.21, easeOutQuint, slidefade
animation = workspacesOut, 1, 1.94, easeOutQuint, slidefade
animation = specialWorkspace, 1, 1.21, quick, slidefadevert
}
# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/
# "Smart gaps" / "No gaps when only"
# uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
# windowrule = bordersize 0, floating:0, onworkspace:f[1]
# windowrule = rounding 0, floating:0, onworkspace:f[1]
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle {
preserve_split = true # You probably want this
force_split = 2
}
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
master {
new_status = master
}
# https://wiki.hyprland.org/Configuring/Variables/#misc
misc {
force_default_wallpaper = 0 # Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = true # If true disables the random hyprland logo / anime girl background. :(
enable_swallow = true
font_family = FontAwesome
}
#############
### INPUT ###
#############
# https://wiki.hyprland.org/Configuring/Variables/#input
input {
kb_layout = us,se,kr
kb_variant = dvorak,dvorak,
kb_model =
kb_options = grp:win_space_toggle,grp:toggle
kb_rules =
follow_mouse = 0
float_switch_override_focus = 0
sensitivity = 0.33 # -1.0 - 1.0, 0 means no modification.
natural_scroll = true
accel_profile = adaptive
touchpad {
natural_scroll = true
tap-to-click = true
disable_while_typing = true
}
}
# https://wiki.hyprland.org/Configuring/Variables/#gestures
gestures {
gesture = 3, horizontal, workspace
gesture = 3, vertical, dispatcher, togglespecialworkspace
}
# Example per-device config
# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more
device {
name = epic-mouse-v1
sensitivity = -0.5
}
###################
### KEYBINDINGS ###
###################
# See https://wiki.hyprland.org/Configuring/Keywords/
$mainMod = SUPER # Sets "Windows" key as main modifier
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
source = ~/.config/hypr/keybindings.conf
# Move focus with mainMod + arrow keys
bind = $mainMod, left, movefocus, l
bind = $mainMod, right, movefocus, r
bind = $mainMod, up, movefocus, u
bind = $mainMod, down, movefocus, d
# Switch workspaces with mainMod + [0-9]
bind = $mainMod, 1, workspace, 1
bind = $mainMod, 2, workspace, 2
bind = $mainMod, 3, workspace, 3
bind = $mainMod, 4, workspace, 4
bind = $mainMod, 5, workspace, 5
bind = $mainMod, 6, workspace, 6
bind = $mainMod, 7, workspace, 7
bind = $mainMod, 8, workspace, 8
bind = $mainMod, 9, workspace, 9
bind = $mainMod, 0, workspace, 10
# Move active window to a workspace with mainMod + SHIFT + [0-9]
bind = $mainMod SHIFT, 1, movetoworkspace, 1
bind = $mainMod SHIFT, 2, movetoworkspace, 2
bind = $mainMod SHIFT, 3, movetoworkspace, 3
bind = $mainMod SHIFT, 4, movetoworkspace, 4
bind = $mainMod SHIFT, 5, movetoworkspace, 5
bind = $mainMod SHIFT, 6, movetoworkspace, 6
bind = $mainMod SHIFT, 7, movetoworkspace, 7
bind = $mainMod SHIFT, 8, movetoworkspace, 8
bind = $mainMod SHIFT, 9, movetoworkspace, 9
bind = $mainMod SHIFT, 0, movetoworkspace, 10
# Example special workspace (scratchpad)
bind = $mainMod, S, togglespecialworkspace
bind = $mainMod SHIFT, S, movetoworkspace, special
# Scroll through existing workspaces with mainMod + scroll
bind = $mainMod, mouse_down, workspace, e+1
bind = $mainMod, mouse_up, workspace, e-1
bind = CTRL ALT, right, workspace, e+1
bind = CTRL ALT, left, workspace, e-1
# Move/resize windows with mainMod + LMB/RMB and dragging
bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow
# Swap active window with the one next to it with SUPER + SHIFT + arrow keys
bindd = SUPER SHIFT, left, Swap window to the left, swapwindow, l
bindd = SUPER SHIFT, right, Swap window to the right, swapwindow, r
bindd = SUPER SHIFT, up, Swap window up, swapwindow, u
bindd = SUPER SHIFT, down, Swap window down, swapwindow, d
# Laptop multimedia keys for volume and LCD brightness
bindel = ,XF86AudioRaiseVolume, exec, ~/.config/hypr/scripts/wob-volume.sh up
bindel = ,XF86AudioLowerVolume, exec, ~/.config/hypr/scripts/wob-volume.sh down
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
bindel = ,XF86MonBrightnessUp, exec, ~/.config/hypr/scripts/wob-brightness.sh up
bindel = ,XF86MonBrightnessDown, exec, ~/.config/hypr/scripts/wob-brightness.sh down
# Requires playerctl
bindl = , XF86AudioNext, exec, playerctl next
bindl = , XF86AudioPause, exec, playerctl play-pause
bindl = , XF86AudioPlay, exec, playerctl play-pause
bindl = , XF86AudioPrev, exec, playerctl previous
##############################
### WINDOWS AND WORKSPACES ###
##############################
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
windowrule {
name = suppress-maximize
match:class = .*
suppress_event = maximize
}
windowrule {
name = xwayland-nofocus-fix
match:class = ^$
match:title = ^$
match:xwayland = 1
match:float = 1
match:fullscreen = 0
match:pin = 0
no_focus = on
}
source = ~/.config/hypr/windowrule.conf
# Host overrides
source = ~/.config/hypr/hosts/host.conf

285
hypr/hyprland.lua Normal file
View file

@ -0,0 +1,285 @@
-- Hyprland Lua config (converted from hyprland.conf)
-- Refer to the wiki for more information: https://wiki.hypr.land/Configuring/Start/
----------------
--## ENVIRONMENT ###
----------------
hl.env("XCURSOR_SIZE", "24")
hl.env("HYPRCURSOR_SIZE", "24")
hl.env("QT_QPA_PLATFORMTHEME", "qt5ct")
hl.env("HYPRSHOT_DIR", "Screenshots")
-----------------
--## MONITORS ###
-----------------
-- Safe default for any machine
hl.monitor({
output = "",
mode = "preferred",
position = "auto",
scale = 1,
})
-------------------
--## MY PROGRAMS ###
-------------------
-- Shared program variables (was programs.conf). Other modules require() this.
local P = require("programs")
--################
--## AUTOSTART ###
--################
-- Hyprland-start executables (from hyprland.conf)
hl.on("hyprland.start", function()
hl.exec_cmd("hypridle")
hl.exec_cmd("waybar")
hl.exec_cmd("swaybg -i ~/.config/hypr/wallpapers/Purple.jpg -m fill")
hl.exec_cmd("~/.config/hypr/scripts/dynamic-borders.sh")
hl.exec_cmd("~/.config/hypr/scripts/xdg-desktop-portal-hyprland.sh")
end)
require("autostart")
-------------------
--## PERMISSIONS ###
-------------------
-- Note: permission changes require a Hyprland restart.
-- ecosystem { enforce_permissions = 1 } (commented out in original)
--####################
--## LOOK AND FEEL ###
--####################
-- General
hl.config({
general = {
gaps_in = 5,
gaps_out = { 1, 0, 0, 0 },
border_size = 3,
col = {
active_border = { colors = { "rgba(D55D7Cee)", "rgba(480023ee)" }, angle = 45 },
inactive_border = "rgba(595959aa)",
},
resize_on_border = true,
allow_tearing = false,
layout = "dwindle",
},
})
-- Decoration
hl.config({
decoration = {
rounding = 10,
rounding_power = 2,
active_opacity = 1.0,
inactive_opacity = 0.8,
shadow = {
enabled = true,
range = 4,
render_power = 3,
color = "rgba(1a1a1aee)",
},
blur = {
enabled = true,
size = 3,
passes = 1,
vibrancy = 0.1696,
},
},
})
-- Animations
hl.config({
animations = {
enabled = true,
},
})
-- Bezier curves
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.0} } })
hl.curve("quick", { type = "bezier", points = { {0.15, 0}, {0.1, 1} } })
-- Animations (default curves)
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 = "easeOutQuint", style = "slidefade" })
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 1.21, bezier = "easeOutQuint", style = "slidefade" })
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 1.94, bezier = "easeOutQuint", style = "slidefade" })
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 1.21, bezier = "quick", style = "slidefadevert" })
-- Dwindle
hl.config({
dwindle = {
preserve_split = true, -- You probably want this
force_split = 2,
},
})
-- Master
hl.config({
master = {
new_status = "master",
},
})
-- Misc
hl.config({
misc = {
force_default_wallpaper = 0, -- Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = true, -- If true disables the random hyprland logo / anime girl background.
enable_swallow = true,
font_family = "FontAwesome",
-- Fix black screen on wake: let the compositor itself re-enable DPMS on input,
-- as a fallback when hypridle's after_sleep dpms-on runs too early / gets ignored.
key_press_enables_dpms = true,
mouse_move_enables_dpms = true,
},
})
---------------
--## INPUT ###
---------------
hl.config({
input = {
kb_layout = "us,se,kr",
kb_variant = "dvorak,dvorak",
kb_options = "grp:win_space_toggle,grp:alt_r_toggle",
follow_mouse = 0,
float_switch_override_focus = 0,
sensitivity = 0.33, -- -1.0 - 1.0, 0 means no modification.
natural_scroll = true,
accel_profile = "adaptive",
touchpad = {
natural_scroll = true,
tap_to_click = true,
disable_while_typing = true,
},
},
})
-- Gestures
hl.gesture({ fingers = 3, direction = "horizontal", action = "workspace" })
hl.gesture({ fingers = 3, direction = "vertical", action = function()
hl.dispatch(hl.dsp.workspace.toggle_special())
end })
-- Example per-device config
hl.device({
name = "epic-mouse-v1",
sensitivity = -0.5,
})
-------------------
--## KEYBINDINGS ###
-------------------
-- Main focus/workspace binds (from hyprland.conf)
local mainMod = "SUPER"
-- Move focus with mainMod + arrow keys
hl.bind(mainMod .. " + left", hl.dsp.focus({ direction = "left" }))
hl.bind(mainMod .. " + right", hl.dsp.focus({ direction = "right" }))
hl.bind(mainMod .. " + up", hl.dsp.focus({ direction = "up" }))
hl.bind(mainMod .. " + down", hl.dsp.focus({ direction = "down" }))
-- Switch workspaces with mainMod + [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 }))
end
-- Move active window to a workspace with mainMod + SHIFT + [0-9]
for i = 1, 10 do
local key = i % 10
hl.bind(mainMod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i }))
end
-- Example special workspace (scratchpad)
hl.bind(mainMod .. " + S", hl.dsp.workspace.toggle_special("magic"))
hl.bind(mainMod .. " + SHIFT + S", hl.dsp.window.move({ workspace = "special:magic" }))
-- 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("CTRL + ALT + right", hl.dsp.focus({ workspace = "e+1" }))
hl.bind("CTRL + ALT + left", 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:273", hl.dsp.window.resize(), { mouse = true })
-- Swap active window with the one next to it with SUPER + SHIFT + arrow keys
hl.bind("SUPER + SHIFT + left", hl.dsp.window.swap({ direction = "left" }), { description = "Swap window to the left" })
hl.bind("SUPER + SHIFT + right", hl.dsp.window.swap({ direction = "right" }), { description = "Swap window to the right" })
hl.bind("SUPER + SHIFT + up", hl.dsp.window.swap({ direction = "up" }), { description = "Swap window up" })
hl.bind("SUPER + SHIFT + down", hl.dsp.window.swap({ direction = "down" }), { description = "Swap window down" })
-- Laptop multimedia keys for volume and LCD brightness (bindel = locked + repeating)
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("~/.config/hypr/scripts/wob-volume.sh up"), { locked = true, repeating = true })
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("~/.config/hypr/scripts/wob-volume.sh down"), { 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("~/.config/hypr/scripts/wob-brightness.sh up"), { locked = true, repeating = true })
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("~/.config/hypr/scripts/wob-brightness.sh down"), { locked = true, repeating = true })
-- Requires playerctl (bindl = locked, no repeat)
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 })
require("keybindings")
---------------------------
--## WINDOWS AND WORKSPACES ###
---------------------------
hl.window_rule({
name = "suppress-maximize",
match = { class = ".*" },
suppress_event = "maximize",
})
hl.window_rule({
name = "xwayland-nofocus-fix",
match = {
class = "^$",
title = "^$",
xwayland = 1,
float = 1,
fullscreen = 0,
pin = 0,
},
no_focus = true,
})
require("windowrule")
-- Host overrides (detect machine by hostname and load matching hosts/<host>.lua)
local hostname = (io.popen("hostname") or {}):read("*l") or ""
hostname = hostname:gsub("%..*$", "") -- strip domain suffix
local ok, err = pcall(require, "hosts." .. hostname)
if not ok then
-- No host file matches this machine; use the defaults already set above.
-- See hosts/example.lua for a template on adding your own host.
end

View file

@ -1,32 +0,0 @@
bind = $mainMod, T, exec, $terminal
bind = $mainMod, B, exec, $browser
bind = $mainMod, W, killactive,
bind = $mainMod SHIFT, M, exit,
bind = $mainMod, E, exec, $fileManager
bind = $mainMod, V, togglefloating,
bindr = $mainMod, SUPER_L, exec, $app_menu
bind = SHIFT, SUPER_L, exec, $menu
bind = $mainMod, P, pseudo, # dwindle
bind = $mainMod, J, layoutmsg, togglesplit # dwindle
bind = $mainMod, F, fullscreen
# Screenshot
bind = CTRL, PRINT, exec, hyprshot -m active -m output
bind = , PRINT, exec, hyprshot -m window
bind = SHIFT, PRINT, exec, hyprshot -m region
# Color picker
bind = $mainMod, C, exec, hyprpicker --autocopy
# Lock
bind = $mainMod, L, exec, ~/.config/hypr/scripts/lock.sh
bind = $mainMod SHIFT, L, exec, wlogout --protocol layer-shell
# Cheatsheet
bind = $mainMod, F1, exec, ~/.config/hypr/scripts/cheatsheet.py ~/.config/hypr/shortcuts.txt
# OBS
bind = $mainMod ALT, TAB, pass, class:^(com.obsproject.Studio)$
bind = , F1, pass, class:^(com.obsproject.Studio)$
bind = , F2, pass, class:^(com.obsproject.Studio)$

38
hypr/keybindings.lua Normal file
View file

@ -0,0 +1,38 @@
-- Keybindings (converted from keybindings.conf)
local P = require("programs")
hl.bind(P.mainMod .. " + T", hl.dsp.exec_cmd(P.terminal))
hl.bind(P.mainMod .. " + B", hl.dsp.exec_cmd(P.browser))
hl.bind(P.mainMod .. " + W", hl.dsp.window.close())
hl.bind(P.mainMod .. " + SHIFT + M", hl.dsp.exit())
hl.bind(P.mainMod .. " + E", hl.dsp.exec_cmd(P.fileManager))
hl.bind(P.mainMod .. " + V", hl.dsp.window.float())
-- App menu (original was bindr = $mainMod, SUPER_L, exec, $app_menu)
hl.bind(P.mainMod .. " + SUPER_L", hl.dsp.exec_cmd(P.app_menu), { release = true })
hl.bind("SHIFT + SUPER_L", hl.dsp.exec_cmd(P.menu))
-- dwindle
hl.bind(P.mainMod .. " + P", hl.dsp.window.pseudo())
-- dwindle
hl.bind(P.mainMod .. " + J", hl.dsp.layout("togglesplit"))
hl.bind(P.mainMod .. " + F", hl.dsp.window.fullscreen())
-- Screenshot
hl.bind("CTRL + PRINT", hl.dsp.exec_cmd("hyprshot -m active -m output"))
hl.bind("PRINT", hl.dsp.exec_cmd("hyprshot -m window"))
hl.bind("SHIFT + PRINT", hl.dsp.exec_cmd("hyprshot -m region"))
-- Color picker
hl.bind(P.mainMod .. " + C", hl.dsp.exec_cmd("hyprpicker --autocopy"))
-- Lock
hl.bind(P.mainMod .. " + L", hl.dsp.exec_cmd("~/.config/hypr/scripts/lock.sh"))
hl.bind(P.mainMod .. " + SHIFT + L", hl.dsp.exec_cmd("wlogout --protocol layer-shell"))
-- Cheatsheet
hl.bind(P.mainMod .. " + F1", hl.dsp.exec_cmd("~/.config/hypr/scripts/cheatsheet.py ~/.config/hypr/shortcuts.txt"))
-- OBS
hl.bind(P.mainMod .. " + ALT + TAB", hl.dsp.pass({ window = "class:^(com.obsproject.Studio)$" }))
hl.bind("F1", hl.dsp.pass({ window = "class:^(com.obsproject.Studio)$" }))
hl.bind("F2", hl.dsp.pass({ window = "class:^(com.obsproject.Studio)$" }))

View file

@ -1,5 +0,0 @@
$terminal = kitty -e zsh -c "fastfetch; exec zsh"
$fileManager = nautilus
$app_menu = ~/.config/hypr/scripts/launch-tofi.sh
$menu = ~/.config/hypr/scripts/launch-menu.sh
$browser = librewolf

14
hypr/programs.lua Normal file
View file

@ -0,0 +1,14 @@
-- Shared program variables (converted from programs.conf)
-- Returned as a table so other modules can require() it.
-- NOTE: inner quotes in `terminal` are significant (preserved from the original).
local P = {
mainMod = "SUPER",
terminal = 'kitty -e zsh -c "fastfetch; exec zsh"',
fileManager = "nautilus",
app_menu = "~/.config/hypr/scripts/launch-tofi.sh",
menu = "~/.config/hypr/scripts/launch-menu.sh",
browser = "librewolf",
}
return P

View file

@ -1,35 +0,0 @@
#!/bin/bash
# Recovery script for when Hyprland display goes black after lock/sleep.
# This resets the display pipeline without killing your session.
# Run from another TTY (Ctrl+Alt+F3).
export XDG_RUNTIME_DIR=/run/user/$(id -u)
# Find the active Hyprland instance by trying each socket until one responds
for SIG in "$XDG_RUNTIME_DIR/hypr"/*/; do
[ -d "$SIG" ] || continue
SIGNAME=$(basename "$SIG")
export HYPRLAND_INSTANCE_SIGNATURE=$SIGNAME
if hyprctl monitors >/dev/null 2>&1; then
FOUND=1
break
fi
done
if [ -z "$FOUND" ]; then
echo "Could not find active Hyprland socket. Is Hyprland running?"
exit 1
fi
echo "Resetting display pipeline..."
pkill hyprlock 2>/dev/null
hyprctl dispatch dpms off
sleep 1
hyprctl dispatch dpms on
# Force a layer tree rebuild to clear orphaned surfaces (e.g. dim_layer)
pkill -x waybar 2>/dev/null
hyprctl dispatch exec waybar 2>/dev/null
echo "Done. Switch back to Hyprland (Ctrl+Alt+F2)."

View file

@ -1,26 +0,0 @@
#!/bin/bash
# hypridle step 3 on-resume — wake display after idle off.
# Cycles DPMS only if the display was actually turned off (avoids flicker
# when the user returns before step 3 fires).
export XDG_RUNTIME_DIR=/run/user/$(id -u)
# Find the active Hyprland socket (not old/stale ones)
for SIG in "$XDG_RUNTIME_DIR/hypr"/*/; do
[ -d "$SIG" ] || continue
SIGNAME=$(basename "$SIG")
export HYPRLAND_INSTANCE_SIGNATURE=$SIGNAME
hyprctl monitors >/dev/null 2>&1 && break
done
DPMS=$(hyprctl monitors 2>/dev/null | grep dpmsStatus | awk '{print $2}')
if [ "$DPMS" = "0" ]; then
# Display was off — cycle DPMS to force re-creation of hyprlock surfaces
hyprctl dispatch dpms off
sleep 1
fi
hyprctl dispatch dpms on
ddcutil setvcp 10 100
wpctl set-mute @DEFAULT_AUDIO_SINK@ 0

View file

@ -1,31 +0,0 @@
# LibreWolf Picture-in-Picture
windowrule {
name = librewolf-pip
match:class = ($browser)
match:title = (Picture-in-Picture)
float = on
pin = on
opacity = 1.0 override 1.0 override
}
# Cheatsheet
windowrule {
name = cheatsheet-window
match:class = net.jeena.Cheatsheet
float = on
center = on
}
windowrule {
name = chromium-ws3
match:class = chromium
workspace = 3 silent
}
windowrule {
name = keepassxc-special
match:class = org.keepassxc.KeePassXC
workspace = special silent
}

34
hypr/windowrule.lua Normal file
View file

@ -0,0 +1,34 @@
-- Window rules (converted from windowrule.conf)
local P = require("programs")
-- LibreWolf Picture-in-Picture
hl.window_rule({
name = "librewolf-pip",
match = {
class = "(" .. P.browser .. ")",
title = "(Picture-in-Picture)",
},
float = true,
pin = true,
opacity = "1.0 override 1.0 override",
})
-- Cheatsheet
hl.window_rule({
name = "cheatsheet-window",
match = { class = "net.jeena.Cheatsheet" },
float = true,
center = true,
})
hl.window_rule({
name = "chromium-ws3",
match = { class = "chromium" },
workspace = "3 silent",
})
hl.window_rule({
name = "keepassxc-special",
match = { class = "org.keepassxc.KeePassXC" },
workspace = "special silent",
})