diff --git a/.gitignore b/.gitignore index b5668e0..ca7c79b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ hypr/menu-*.list +hypr/hosts/* +!hpyr/hosts/example-host.conf diff --git a/hypr/autostart.conf b/hypr/autostart.conf new file mode 100644 index 0000000..4f2da34 --- /dev/null +++ b/hypr/autostart.conf @@ -0,0 +1,11 @@ +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" diff --git a/hypr/autostart.lua b/hypr/autostart.lua deleted file mode 100644 index bf37e92..0000000 --- a/hypr/autostart.lua +++ /dev/null @@ -1,16 +0,0 @@ --- 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) diff --git a/hypr/hosts/Chester.lua b/hypr/hosts/Chester.lua deleted file mode 100644 index b65fdb3..0000000 --- a/hypr/hosts/Chester.lua +++ /dev/null @@ -1,11 +0,0 @@ --- 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, -}) diff --git a/hypr/hosts/Rutherford.lua b/hypr/hosts/Rutherford.lua deleted file mode 100644 index 9b8097b..0000000 --- a/hypr/hosts/Rutherford.lua +++ /dev/null @@ -1,9 +0,0 @@ --- 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, -}) diff --git a/hypr/hosts/example.lua b/hypr/hosts/example.lua deleted file mode 100644 index 829a8b5..0000000 --- a/hypr/hosts/example.lua +++ /dev/null @@ -1,8 +0,0 @@ --- example.lua — TEMPLATE for a per-host config file (NOT loaded at runtime). --- --- Hyprland's Lua config only loads hosts/.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/.lua and fill it --- in. The hostname lookup strips any domain suffix (e.g. "laptop.home" -> "laptop"). diff --git a/hypr/hypridle.conf b/hypr/hypridle.conf index 357fd14..b452eab 100644 --- a/hypr/hypridle.conf +++ b/hypr/hypridle.conf @@ -2,21 +2,13 @@ general { inhibit_sleep = 3 lock_cmd = pidof hyprlock || ~/.config/hypr/scripts/lock.sh before_sleep_cmd = loginctl lock-session - # 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' + # 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 } -# 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 +# Step 1: Lock screen at 2 min idle listener { timeout = 120 on-timeout = ~/.config/hypr/scripts/lock.sh @@ -35,13 +27,19 @@ listener { # Both together ensure the screen is fully off visually and electrically # When the user becomes active again, restore display - on-resume = hyprctl dispatch dpms on && ddcutil setvcp 10 100 && wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 + # + # 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 # Explanation: - # - hyprctl dispatch dpms on → ensures the display is powered on by the compositor + # - wake-display.sh checks if dpms is off; only then does a dpms off/on + # cycle to force Hyprland to recreate hyprlock surfaces # - ddcutil setvcp 10 100 → restores monitor brightness to full # - # Both together guarantee the screen wakes reliably on all monitors + # Both together guarantee the screen wakes reliably } # Step 4: Suspend system after 30 min idle diff --git a/hypr/hyprland.conf b/hypr/hyprland.conf new file mode 100644 index 0000000..962572e --- /dev/null +++ b/hypr/hyprland.conf @@ -0,0 +1,331 @@ +# 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 diff --git a/hypr/hyprland.lua b/hypr/hyprland.lua deleted file mode 100644 index 2fd3761..0000000 --- a/hypr/hyprland.lua +++ /dev/null @@ -1,285 +0,0 @@ --- 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/.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 diff --git a/hypr/keybindings.conf b/hypr/keybindings.conf new file mode 100644 index 0000000..acf0d7d --- /dev/null +++ b/hypr/keybindings.conf @@ -0,0 +1,32 @@ +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)$ diff --git a/hypr/keybindings.lua b/hypr/keybindings.lua deleted file mode 100644 index b45c0bc..0000000 --- a/hypr/keybindings.lua +++ /dev/null @@ -1,38 +0,0 @@ --- 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)$" })) diff --git a/hypr/programs.conf b/hypr/programs.conf new file mode 100644 index 0000000..66270e7 --- /dev/null +++ b/hypr/programs.conf @@ -0,0 +1,5 @@ +$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 diff --git a/hypr/programs.lua b/hypr/programs.lua deleted file mode 100644 index c87fae9..0000000 --- a/hypr/programs.lua +++ /dev/null @@ -1,14 +0,0 @@ --- 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 diff --git a/hypr/scripts/unstick-display.sh b/hypr/scripts/unstick-display.sh new file mode 100755 index 0000000..07572ae --- /dev/null +++ b/hypr/scripts/unstick-display.sh @@ -0,0 +1,35 @@ +#!/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)." diff --git a/hypr/scripts/wake-display.sh b/hypr/scripts/wake-display.sh new file mode 100755 index 0000000..0930018 --- /dev/null +++ b/hypr/scripts/wake-display.sh @@ -0,0 +1,26 @@ +#!/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 diff --git a/hypr/windowrule.conf b/hypr/windowrule.conf new file mode 100644 index 0000000..80ccd65 --- /dev/null +++ b/hypr/windowrule.conf @@ -0,0 +1,31 @@ +# 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 +} diff --git a/hypr/windowrule.lua b/hypr/windowrule.lua deleted file mode 100644 index c0123a6..0000000 --- a/hypr/windowrule.lua +++ /dev/null @@ -1,34 +0,0 @@ --- 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", -})