From c75d1e59e0f144d00ea76d56e72af017faa5a787 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Tue, 3 Feb 2026 20:15:54 -0800 Subject: [PATCH] hyprland: update config and workspace script --- dotfiles/config/hypr/hyprland.conf | 60 +++++++++---------- .../hypr/scripts/shift-to-empty-on-screen.sh | 21 +++++-- nixos/keyd.nix | 39 ++++++++++++ 3 files changed, 84 insertions(+), 36 deletions(-) create mode 100644 nixos/keyd.nix diff --git a/dotfiles/config/hypr/hyprland.conf b/dotfiles/config/hypr/hyprland.conf index b97b4e80..f11358d1 100644 --- a/dotfiles/config/hypr/hyprland.conf +++ b/dotfiles/config/hypr/hyprland.conf @@ -5,9 +5,8 @@ # ============================================================================= # PLUGINS (Hyprland pinned to 0.53.0 to match hy3) # ============================================================================= -exec-once = hyprctl plugin load /run/current-system/sw/lib/libhy3.so -# hyprexpo disabled - header compatibility issue -# exec-once = hyprctl plugin load /run/current-system/sw/lib/libhyprexpo.so +# Load the plugin before parsing keybinds/layouts that depend on it +plugin = /run/current-system/sw/lib/libhy3.so # ============================================================================= # MONITORS @@ -106,17 +105,22 @@ master { new_status = slave mfact = 0.5 orientation = left - # Smart gaps - no gaps when only one window - no_gaps_when_only = 1 } # Dwindle layout (alternative - binary tree like i3) dwindle { pseudotile = yes preserve_split = yes - no_gaps_when_only = 1 } +# ============================================================================= +# WORKSPACE RULES (SMART GAPS) +# ============================================================================= +# Replace no_gaps_when_only (removed in newer Hyprland) +# Remove gaps when there's only one visible tiled window (ignore special workspaces) +workspace = w[tv1]s[false], gapsout:0, gapsin:0 +workspace = f[1]s[false], gapsout:0, gapsin:0 + # Group/tabbed window configuration (built-in alternative to hy3 tabs) group { col.border_active = rgba(edb443ff) @@ -159,17 +163,16 @@ plugin { } } - # hyprexpo disabled - header compatibility issue - # hyprexpo { - # columns = 3 - # gap_size = 5 - # bg_col = rgba(000000ff) - # workspace_method = first 1 - # enable_gesture = true - # gesture_fingers = 3 - # gesture_distance = 300 - # gesture_positive = false - # } + hyprexpo { + columns = 3 + gap_size = 5 + bg_col = rgba(000000ff) + workspace_method = first 1 + enable_gesture = true + gesture_fingers = 3 + gesture_distance = 300 + gesture_positive = false + } } # ============================================================================= @@ -287,11 +290,11 @@ bind = $mainMod, S, hy3:movefocus, d bind = $mainMod, A, hy3:movefocus, l bind = $mainMod, D, hy3:movefocus, r -# Move windows (Mod + Shift + WASD) - hy3:movewindow with once=true for swapping -bind = $mainMod SHIFT, W, hy3:movewindow, u, once -bind = $mainMod SHIFT, S, hy3:movewindow, d, once -bind = $mainMod SHIFT, A, hy3:movewindow, l, once -bind = $mainMod SHIFT, D, hy3:movewindow, r, once +# Move windows (Mod + Shift + WASD), keep focus, and warp cursor to the moved window +bind = $mainMod SHIFT, W, exec, sh -c 'addr=$(hyprctl -j activewindow | jq -r .address); hyprctl dispatch hy3:movewindow u once; [ -n "$addr" ] && hyprctl dispatch focuswindow address:$addr' +bind = $mainMod SHIFT, S, exec, sh -c 'addr=$(hyprctl -j activewindow | jq -r .address); hyprctl dispatch hy3:movewindow d once; [ -n "$addr" ] && hyprctl dispatch focuswindow address:$addr' +bind = $mainMod SHIFT, A, exec, sh -c 'addr=$(hyprctl -j activewindow | jq -r .address); hyprctl dispatch hy3:movewindow l once; [ -n "$addr" ] && hyprctl dispatch focuswindow address:$addr' +bind = $mainMod SHIFT, D, exec, sh -c 'addr=$(hyprctl -j activewindow | jq -r .address); hyprctl dispatch hy3:movewindow r once; [ -n "$addr" ] && hyprctl dispatch focuswindow address:$addr' # Resize windows (Mod + Ctrl + WASD) binde = $mainMod CTRL, W, resizeactive, 0 -50 @@ -340,9 +343,9 @@ bind = $mainMod SHIFT, slash, hy3:changegroup, v bind = $mainMod, bracketright, hy3:focustab, r, wrap bind = $mainMod, bracketleft, hy3:focustab, l, wrap -# Move window within tab group -bind = $mainMod SHIFT, bracketright, hy3:movetab, r -bind = $mainMod SHIFT, bracketleft, hy3:movetab, l +# Move window within tab group (hy3 has no movetab dispatcher) +bind = $mainMod SHIFT, bracketright, hy3:movewindow, r, visible +bind = $mainMod SHIFT, bracketleft, hy3:movewindow, l, visible # Expand focus to parent group (like XMonad's focus parent) bind = $mainMod, grave, hy3:expand, expand @@ -495,13 +498,10 @@ bind = $hyper, K, exec, rofi_kill_process.sh bind = $hyper SHIFT, K, exec, rofi_kill_all.sh bind = $hyper, R, exec, rofi-systemd bind = $hyper, 9, exec, start_synergy.sh -# Overview - show all windows (Hyper+Space) -# hyprexpo disabled - using rofi window switcher as fallback -bind = $hyper, Space, exec, rofi -show window -show-icons +# Workspace overview (Hyper+Space) +bind = $hyper, Space, hyprexpo:expo, toggle bind = $hyper, I, exec, rofi_select_input.hs bind = $hyper, O, exec, rofi_paswitch -bind = $mainMod, apostrophe, exec, load_default_map -bind = $modAlt, apostrophe, exec, load_xkb_map # Reload config bind = $mainMod, R, exec, hyprctl reload diff --git a/dotfiles/config/hypr/scripts/shift-to-empty-on-screen.sh b/dotfiles/config/hypr/scripts/shift-to-empty-on-screen.sh index fcc76dca..19ceb3c1 100755 --- a/dotfiles/config/hypr/scripts/shift-to-empty-on-screen.sh +++ b/dotfiles/config/hypr/scripts/shift-to-empty-on-screen.sh @@ -7,12 +7,20 @@ set -euo pipefail DIRECTION="$1" -# First, move focus to the screen in that direction +# Track the current monitor so we can return +ORIG_MONITOR=$(hyprctl activeworkspace -j | jq -r '.monitor') + +# Move focus to the screen in that direction hyprctl dispatch focusmonitor "$DIRECTION" -# Get the monitor we're now on +# Get the monitor we're now on (target monitor) MONITOR=$(hyprctl activeworkspace -j | jq -r '.monitor') +# If there is no monitor in that direction, bail +if [ "$MONITOR" = "$ORIG_MONITOR" ]; then + exit 0 +fi + # Find an empty workspace or create one # First check if there's an empty workspace on this monitor EMPTY_WS=$(hyprctl workspaces -j | jq -r ".[] | select(.windows == 0 and .monitor == \"$MONITOR\") | .id" | head -1) @@ -23,8 +31,9 @@ if [ -z "$EMPTY_WS" ]; then EMPTY_WS=$((MAX_WS + 1)) fi -# Go back to original monitor and move the window -hyprctl dispatch focusmonitor "$DIRECTION" # This actually toggles back +# Ensure the workspace exists on the target monitor +hyprctl dispatch workspace "$EMPTY_WS" -# Move window to the empty workspace and follow -hyprctl dispatch movetoworkspace "$EMPTY_WS" +# Go back to original monitor and move the window (without following) +hyprctl dispatch focusmonitor "$ORIG_MONITOR" +hyprctl dispatch movetoworkspacesilent "$EMPTY_WS" diff --git a/nixos/keyd.nix b/nixos/keyd.nix new file mode 100644 index 00000000..ddba831a --- /dev/null +++ b/nixos/keyd.nix @@ -0,0 +1,39 @@ +{ config, lib, makeEnable, ... }: +makeEnable config "myModules.keyd" true { + services.keyd = { + enable = true; + + # Base remap applied to all keyboards. + keyboards.default = { + # Exclude the Glove80 (MoErgo) by vendor:product ID. + ids = [ "*" "-16c0:27db" ]; + settings = { + main = { + # Caps Lock -> Control + capslock = "leftcontrol"; + # Swap Left Alt and Left Super + leftalt = "leftmeta"; + leftmeta = "leftalt"; + # Right Alt -> Hyper chord + rightalt = "layer(hyper)"; + }; + # Hyper = Ctrl+Alt+Meta+Shift while held + "hyper:C-A-M-S" = { }; + }; + }; + + # TODO: Add per-keyboard overrides here once device IDs are known. + # Example: + # keyboards.externalKinesis = { + # ids = [ "1ea7:0907" ]; + # settings = { + # main = { + # leftalt = "leftmeta"; + # leftmeta = "leftalt"; + # rightalt = "layer(hyper)"; + # }; + # "hyper:C-A-M-S" = { }; + # }; + # }; + }; +}