hyprland: cap workspaces and add empty-workspace helpers
- Introduce HYPR_MAX_WORKSPACE (default 9) and enforce it in scripts - Replace 'workspace empty' bindings with scripts that pick an empty id - Add scroll-to-workspace helper for mouse wheel binds
This commit is contained in:
@@ -27,6 +27,8 @@ $runMenu = rofi -show run
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
env = XCURSOR_SIZE,24
|
env = XCURSOR_SIZE,24
|
||||||
env = QT_QPA_PLATFORMTHEME,qt5ct
|
env = QT_QPA_PLATFORMTHEME,qt5ct
|
||||||
|
# Used by ~/.config/hypr/scripts/* to keep workspace IDs bounded.
|
||||||
|
env = HYPR_MAX_WORKSPACE,9
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# INPUT CONFIGURATION
|
# INPUT CONFIGURATION
|
||||||
@@ -405,7 +407,7 @@ bind = $modAlt, Return, togglespecialworkspace, minimized
|
|||||||
# WORKSPACE CONTROL
|
# WORKSPACE CONTROL
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
# Switch workspaces (1-9, 0=10)
|
# Switch workspaces (1-9 only)
|
||||||
bind = $mainMod, 1, workspace, 1
|
bind = $mainMod, 1, workspace, 1
|
||||||
bind = $mainMod, 2, workspace, 2
|
bind = $mainMod, 2, workspace, 2
|
||||||
bind = $mainMod, 3, workspace, 3
|
bind = $mainMod, 3, workspace, 3
|
||||||
@@ -415,7 +417,6 @@ bind = $mainMod, 6, workspace, 6
|
|||||||
bind = $mainMod, 7, workspace, 7
|
bind = $mainMod, 7, workspace, 7
|
||||||
bind = $mainMod, 8, workspace, 8
|
bind = $mainMod, 8, workspace, 8
|
||||||
bind = $mainMod, 9, workspace, 9
|
bind = $mainMod, 9, workspace, 9
|
||||||
bind = $mainMod, 0, workspace, 10
|
|
||||||
|
|
||||||
# Move window to workspace
|
# Move window to workspace
|
||||||
bind = $mainMod SHIFT, 1, movetoworkspace, 1
|
bind = $mainMod SHIFT, 1, movetoworkspace, 1
|
||||||
@@ -427,7 +428,6 @@ bind = $mainMod SHIFT, 6, movetoworkspace, 6
|
|||||||
bind = $mainMod SHIFT, 7, movetoworkspace, 7
|
bind = $mainMod SHIFT, 7, movetoworkspace, 7
|
||||||
bind = $mainMod SHIFT, 8, movetoworkspace, 8
|
bind = $mainMod SHIFT, 8, movetoworkspace, 8
|
||||||
bind = $mainMod SHIFT, 9, movetoworkspace, 9
|
bind = $mainMod SHIFT, 9, movetoworkspace, 9
|
||||||
bind = $mainMod SHIFT, 0, movetoworkspace, 10
|
|
||||||
|
|
||||||
# Move and follow to workspace (like XMonad's shiftThenView)
|
# Move and follow to workspace (like XMonad's shiftThenView)
|
||||||
bind = $mainMod CTRL, 1, movetoworkspacesilent, 1
|
bind = $mainMod CTRL, 1, movetoworkspacesilent, 1
|
||||||
@@ -448,8 +448,6 @@ bind = $mainMod CTRL, 8, movetoworkspacesilent, 8
|
|||||||
bind = $mainMod CTRL, 8, workspace, 8
|
bind = $mainMod CTRL, 8, workspace, 8
|
||||||
bind = $mainMod CTRL, 9, movetoworkspacesilent, 9
|
bind = $mainMod CTRL, 9, movetoworkspacesilent, 9
|
||||||
bind = $mainMod CTRL, 9, workspace, 9
|
bind = $mainMod CTRL, 9, workspace, 9
|
||||||
bind = $mainMod CTRL, 0, movetoworkspacesilent, 10
|
|
||||||
bind = $mainMod CTRL, 0, workspace, 10
|
|
||||||
|
|
||||||
# Workspace cycling (like XMonad's cycleWorkspaceOnCurrentScreen)
|
# Workspace cycling (like XMonad's cycleWorkspaceOnCurrentScreen)
|
||||||
bind = $mainMod, backslash, exec, ~/.config/hypr/scripts/workspace-back.sh
|
bind = $mainMod, backslash, exec, ~/.config/hypr/scripts/workspace-back.sh
|
||||||
@@ -458,14 +456,14 @@ bind = $mainMod, backslash, exec, ~/.config/hypr/scripts/workspace-back.sh
|
|||||||
bind = $hyper, 5, exec, ~/.config/hypr/scripts/swap-workspaces.sh
|
bind = $hyper, 5, exec, ~/.config/hypr/scripts/swap-workspaces.sh
|
||||||
|
|
||||||
# Go to next empty workspace (like XMonad's moveTo Next emptyWS)
|
# Go to next empty workspace (like XMonad's moveTo Next emptyWS)
|
||||||
bind = $hyper, E, workspace, empty
|
bind = $hyper, E, exec, ~/.config/hypr/scripts/workspace-goto-empty.sh
|
||||||
|
|
||||||
# Move to next screen (like XMonad's shiftToNextScreenX)
|
# Move to next screen (like XMonad's shiftToNextScreenX)
|
||||||
bind = $mainMod, Z, focusmonitor, +1
|
bind = $mainMod, Z, focusmonitor, +1
|
||||||
bind = $mainMod SHIFT, Z, movewindow, mon:+1
|
bind = $mainMod SHIFT, Z, movewindow, mon:+1
|
||||||
|
|
||||||
# Shift to empty workspace and view (like XMonad's shiftToEmptyAndView)
|
# Shift to empty workspace and view (like XMonad's shiftToEmptyAndView)
|
||||||
bind = $mainMod SHIFT, H, movetoworkspace, empty
|
bind = $mainMod SHIFT, H, exec, ~/.config/hypr/scripts/workspace-move-to-empty.sh
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# WINDOW MANAGEMENT
|
# WINDOW MANAGEMENT
|
||||||
@@ -543,8 +541,8 @@ bindm = $mainMod, mouse:272, movewindow
|
|||||||
bindm = $mainMod, mouse:273, resizewindow
|
bindm = $mainMod, mouse:273, resizewindow
|
||||||
|
|
||||||
# Scroll through workspaces
|
# Scroll through workspaces
|
||||||
bind = $mainMod, mouse_down, workspace, e+1
|
bind = $mainMod, mouse_down, exec, ~/.config/hypr/scripts/workspace-scroll.sh +1
|
||||||
bind = $mainMod, mouse_up, workspace, e-1
|
bind = $mainMod, mouse_up, exec, ~/.config/hypr/scripts/workspace-scroll.sh -1
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# AUTOSTART
|
# AUTOSTART
|
||||||
|
|||||||
72
dotfiles/config/hypr/scripts/find-empty-workspace.sh
Executable file
72
dotfiles/config/hypr/scripts/find-empty-workspace.sh
Executable file
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Print an "empty" workspace id within 1..$HYPR_MAX_WORKSPACE (default 9).
|
||||||
|
#
|
||||||
|
# Preference order (lowest id wins within each tier):
|
||||||
|
# 1. Workspace exists on the target monitor and has 0 windows
|
||||||
|
# 2. Workspace id does not exist at all (will be created on dispatch)
|
||||||
|
# 3. Workspace exists (elsewhere) and has 0 windows
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# find-empty-workspace.sh [monitor] [exclude_id]
|
||||||
|
|
||||||
|
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||||
|
|
||||||
|
monitor="${1:-}"
|
||||||
|
exclude_id="${2:-}"
|
||||||
|
|
||||||
|
if [[ -z "${monitor}" ]]; then
|
||||||
|
monitor="$(hyprctl activeworkspace -j | jq -r '.monitor' 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${monitor}" || "${monitor}" == "null" ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
workspaces_json="$(hyprctl workspaces -j 2>/dev/null || echo '[]')"
|
||||||
|
|
||||||
|
unused_candidate=""
|
||||||
|
elsewhere_empty_candidate=""
|
||||||
|
|
||||||
|
for i in $(seq 1 "${max_ws}"); do
|
||||||
|
if [[ -n "${exclude_id}" && "${i}" == "${exclude_id}" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
exists="$(jq -r --argjson id "${i}" '[.[] | select(.id == $id)] | length' <<<"${workspaces_json}")"
|
||||||
|
if [[ "${exists}" == "0" ]]; then
|
||||||
|
if [[ -z "${unused_candidate}" ]]; then
|
||||||
|
unused_candidate="${i}"
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
windows="$(jq -r --argjson id "${i}" '([.[] | select(.id == $id) | .windows] | .[0]) // 0' <<<"${workspaces_json}")"
|
||||||
|
if [[ "${windows}" != "0" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
ws_monitor="$(jq -r --argjson id "${i}" '([.[] | select(.id == $id) | .monitor] | .[0]) // ""' <<<"${workspaces_json}")"
|
||||||
|
if [[ "${ws_monitor}" == "${monitor}" ]]; then
|
||||||
|
printf '%s\n' "${i}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${elsewhere_empty_candidate}" ]]; then
|
||||||
|
elsewhere_empty_candidate="${i}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "${unused_candidate}" ]]; then
|
||||||
|
printf '%s\n' "${unused_candidate}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${elsewhere_empty_candidate}" ]]; then
|
||||||
|
printf '%s\n' "${elsewhere_empty_candidate}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
DIRECTION="$1"
|
DIRECTION="$1"
|
||||||
|
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||||
|
|
||||||
# Track the current monitor so we can return
|
# Track the current monitor so we can return
|
||||||
ORIG_MONITOR=$(hyprctl activeworkspace -j | jq -r '.monitor')
|
ORIG_MONITOR=$(hyprctl activeworkspace -j | jq -r '.monitor')
|
||||||
@@ -21,14 +22,17 @@ if [ "$MONITOR" = "$ORIG_MONITOR" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Find an empty workspace or create one
|
# Find an empty workspace within 1..$HYPR_MAX_WORKSPACE.
|
||||||
# First check if there's an empty workspace on this monitor
|
EMPTY_WS="$(~/.config/hypr/scripts/find-empty-workspace.sh "${MONITOR}" 2>/dev/null || true)"
|
||||||
EMPTY_WS=$(hyprctl workspaces -j | jq -r ".[] | select(.windows == 0 and .monitor == \"$MONITOR\") | .id" | head -1)
|
if [[ -z "${EMPTY_WS}" ]]; then
|
||||||
|
# No empty workspace available within the cap; restore focus and bail.
|
||||||
|
hyprctl dispatch focusmonitor "$ORIG_MONITOR"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$EMPTY_WS" ]; then
|
if (( EMPTY_WS < 1 || EMPTY_WS > max_ws )); then
|
||||||
# No empty workspace, find next available workspace number
|
hyprctl dispatch focusmonitor "$ORIG_MONITOR"
|
||||||
MAX_WS=$(hyprctl workspaces -j | jq -r 'map(.id) | max')
|
exit 0
|
||||||
EMPTY_WS=$((MAX_WS + 1))
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure the workspace exists on the target monitor
|
# Ensure the workspace exists on the target monitor
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||||
|
|
||||||
CURRENT_WS="$(hyprctl activeworkspace -j | jq -r '.id')"
|
CURRENT_WS="$(hyprctl activeworkspace -j | jq -r '.id')"
|
||||||
if [[ -z "${CURRENT_WS}" || "${CURRENT_WS}" == "null" ]]; then
|
if [[ -z "${CURRENT_WS}" || "${CURRENT_WS}" == "null" ]]; then
|
||||||
exit 0
|
exit 0
|
||||||
@@ -13,7 +15,7 @@ TARGET_WS="${1:-}"
|
|||||||
|
|
||||||
if [[ -z "${TARGET_WS}" ]]; then
|
if [[ -z "${TARGET_WS}" ]]; then
|
||||||
WS_LIST="$({
|
WS_LIST="$({
|
||||||
seq 1 10
|
seq 1 "${max_ws}"
|
||||||
hyprctl workspaces -j | jq -r '.[].id' 2>/dev/null || true
|
hyprctl workspaces -j | jq -r '.[].id' 2>/dev/null || true
|
||||||
} | awk 'NF {print $1}' | awk '!seen[$0]++' | sort -n)"
|
} | awk 'NF {print $1}' | awk '!seen[$0]++' | sort -n)"
|
||||||
|
|
||||||
@@ -33,6 +35,11 @@ if ! [[ "${TARGET_WS}" =~ ^-?[0-9]+$ ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if (( TARGET_WS < 1 || TARGET_WS > max_ws )); then
|
||||||
|
notify-send "Swap Workspace" "Workspace out of range (1-${max_ws}): ${TARGET_WS}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
WINDOWS_CURRENT="$(hyprctl clients -j | jq -r --arg ws "${CURRENT_WS}" '.[] | select((.workspace.id|tostring) == $ws) | .address')"
|
WINDOWS_CURRENT="$(hyprctl clients -j | jq -r --arg ws "${CURRENT_WS}" '.[] | select((.workspace.id|tostring) == $ws) | .address')"
|
||||||
WINDOWS_TARGET="$(hyprctl clients -j | jq -r --arg ws "${TARGET_WS}" '.[] | select((.workspace.id|tostring) == $ws) | .address')"
|
WINDOWS_TARGET="$(hyprctl clients -j | jq -r --arg ws "${TARGET_WS}" '.[] | select((.workspace.id|tostring) == $ws) | .address')"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||||
|
|
||||||
runtime_dir="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
runtime_dir="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
||||||
state_dir="${runtime_dir}/hypr"
|
state_dir="${runtime_dir}/hypr"
|
||||||
prev_file="${state_dir}/prev-workspace"
|
prev_file="${state_dir}/prev-workspace"
|
||||||
@@ -10,4 +12,8 @@ if [[ -z "${prev}" ]]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "${prev}" =~ ^[0-9]+$ ]] && (( prev < 1 || prev > max_ws )); then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
hyprctl dispatch workspace "${prev}" >/dev/null 2>&1 || true
|
hyprctl dispatch workspace "${prev}" >/dev/null 2>&1 || true
|
||||||
|
|||||||
16
dotfiles/config/hypr/scripts/workspace-goto-empty.sh
Executable file
16
dotfiles/config/hypr/scripts/workspace-goto-empty.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cur_ws="$(hyprctl activeworkspace -j | jq -r '.id' 2>/dev/null || true)"
|
||||||
|
monitor="$(hyprctl activeworkspace -j | jq -r '.monitor' 2>/dev/null || true)"
|
||||||
|
|
||||||
|
ws="$(
|
||||||
|
~/.config/hypr/scripts/find-empty-workspace.sh "${monitor}" "${cur_ws}" 2>/dev/null || true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "${ws}" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyprctl dispatch workspace "${ws}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||||
|
|
||||||
runtime_dir="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
runtime_dir="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
||||||
sig="${HYPRLAND_INSTANCE_SIGNATURE:-}"
|
sig="${HYPRLAND_INSTANCE_SIGNATURE:-}"
|
||||||
if [[ -z "$sig" ]]; then
|
if [[ -z "$sig" ]]; then
|
||||||
@@ -45,6 +47,11 @@ nc -U "${sock}" | while read -r line; do
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Ignore workspaces outside the configured cap.
|
||||||
|
if [[ "${ws_id}" =~ ^[0-9]+$ ]] && (( ws_id < 1 || ws_id > max_ws )); then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
ws_ident="${ws_name}"
|
ws_ident="${ws_name}"
|
||||||
if [[ -z "${ws_ident}" ]]; then
|
if [[ -z "${ws_ident}" ]]; then
|
||||||
ws_ident="${ws_id}"
|
ws_ident="${ws_id}"
|
||||||
|
|||||||
16
dotfiles/config/hypr/scripts/workspace-move-to-empty.sh
Executable file
16
dotfiles/config/hypr/scripts/workspace-move-to-empty.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cur_ws="$(hyprctl activeworkspace -j | jq -r '.id' 2>/dev/null || true)"
|
||||||
|
monitor="$(hyprctl activeworkspace -j | jq -r '.monitor' 2>/dev/null || true)"
|
||||||
|
|
||||||
|
ws="$(
|
||||||
|
~/.config/hypr/scripts/find-empty-workspace.sh "${monitor}" "${cur_ws}" 2>/dev/null || true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "${ws}" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyprctl dispatch movetoworkspace "${ws}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
42
dotfiles/config/hypr/scripts/workspace-scroll.sh
Executable file
42
dotfiles/config/hypr/scripts/workspace-scroll.sh
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||||
|
delta="${1:-}"
|
||||||
|
|
||||||
|
case "${delta}" in
|
||||||
|
+1|-1) ;;
|
||||||
|
next) delta="+1" ;;
|
||||||
|
prev) delta="-1" ;;
|
||||||
|
*)
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
cur="$(hyprctl activeworkspace -j | jq -r '.id' 2>/dev/null || true)"
|
||||||
|
if ! [[ "${cur}" =~ ^[0-9]+$ ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( cur < 1 )); then
|
||||||
|
cur=1
|
||||||
|
elif (( cur > max_ws )); then
|
||||||
|
cur="${max_ws}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${delta}" == "+1" ]]; then
|
||||||
|
if (( cur >= max_ws )); then
|
||||||
|
nxt=1
|
||||||
|
else
|
||||||
|
nxt=$((cur + 1))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if (( cur <= 1 )); then
|
||||||
|
nxt="${max_ws}"
|
||||||
|
else
|
||||||
|
nxt=$((cur - 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyprctl dispatch workspace "${nxt}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
Reference in New Issue
Block a user