From 3a405c0520d397ecb1d99cdbafec57805531049b Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Wed, 4 Feb 2026 02:47:12 -0800 Subject: [PATCH] hyprland: restore mod+backslash workspace toggle --- dotfiles/config/hypr/hyprland.conf | 15 ++++- .../config/hypr/scripts/workspace-back.sh | 13 ++++ .../config/hypr/scripts/workspace-history.sh | 60 +++++++++++++++++++ 3 files changed, 86 insertions(+), 2 deletions(-) create mode 100755 dotfiles/config/hypr/scripts/workspace-back.sh create mode 100755 dotfiles/config/hypr/scripts/workspace-history.sh diff --git a/dotfiles/config/hypr/hyprland.conf b/dotfiles/config/hypr/hyprland.conf index 7a850c4e..bdc516c6 100644 --- a/dotfiles/config/hypr/hyprland.conf +++ b/dotfiles/config/hypr/hyprland.conf @@ -185,6 +185,15 @@ misc { disable_hyprland_logo = true } +# ============================================================================= +# BINDS OPTIONS +# ============================================================================= +binds { + # Keep workspace history so "previous" can toggle back reliably. + allow_workspace_cycles = true + workspace_back_and_forth = true +} + # ============================================================================= # WINDOW RULES # ============================================================================= @@ -348,7 +357,8 @@ bind = $mainMod SHIFT, Space, hy3:changegroup, opposite # Create specific group types bind = $mainMod, H, hy3:makegroup, h bind = $mainMod SHIFT, V, hy3:makegroup, v -bind = $mainMod CTRL, Space, fullscreen, 0 +# Mod+Ctrl+Space mirrors Mod+Space (tabs instead of fullscreen) +bind = $mainMod CTRL, Space, hy3:changegroup, toggletab # Change group type (cycle h -> v -> tab) bind = $mainMod, slash, hy3:changegroup, h @@ -442,7 +452,7 @@ bind = $mainMod CTRL, 0, movetoworkspacesilent, 10 bind = $mainMod CTRL, 0, workspace, 10 # Workspace cycling (like XMonad's cycleWorkspaceOnCurrentScreen) -bind = $mainMod, backslash, workspace, previous +bind = $mainMod, backslash, exec, ~/.config/hypr/scripts/workspace-back.sh # Go to next empty workspace (like XMonad's moveTo Next emptyWS) bind = $hyper, E, workspace, empty @@ -540,6 +550,7 @@ bind = $mainMod, mouse_up, workspace, e-1 # Start Hyprland session target for systemd user services (e.g., waybar) exec-once = systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE exec-once = systemctl --user start hyprland-session.target +exec-once = ~/.config/hypr/scripts/workspace-history.sh # Scratchpad applications (spawn on demand via keybinds) # exec-once = [workspace special:element silent] element-desktop diff --git a/dotfiles/config/hypr/scripts/workspace-back.sh b/dotfiles/config/hypr/scripts/workspace-back.sh new file mode 100755 index 00000000..94caf7bd --- /dev/null +++ b/dotfiles/config/hypr/scripts/workspace-back.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +runtime_dir="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" +state_dir="${runtime_dir}/hypr" +prev_file="${state_dir}/prev-workspace" + +prev="$(cat "${prev_file}" 2>/dev/null || true)" +if [[ -z "${prev}" ]]; then + exit 0 +fi + +hyprctl dispatch workspace "${prev}" >/dev/null 2>&1 || true diff --git a/dotfiles/config/hypr/scripts/workspace-history.sh b/dotfiles/config/hypr/scripts/workspace-history.sh new file mode 100755 index 00000000..396b4d6c --- /dev/null +++ b/dotfiles/config/hypr/scripts/workspace-history.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +set -euo pipefail + +runtime_dir="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" +sig="${HYPRLAND_INSTANCE_SIGNATURE:-}" +if [[ -z "$sig" ]]; then + exit 0 +fi + +sock="${runtime_dir}/hypr/${sig}/.socket2.sock" +state_dir="${runtime_dir}/hypr" +last_file="${state_dir}/last-workspace" +prev_file="${state_dir}/prev-workspace" + +mkdir -p "${state_dir}" + +# Initialize current workspace to avoid empty state. +if command -v hyprctl >/dev/null 2>&1; then + cur_id="$(hyprctl activeworkspace -j | jq -r '.id' 2>/dev/null || true)" + if [[ -n "${cur_id}" && "${cur_id}" != "null" ]]; then + echo "${cur_id}" > "${last_file}" + fi +fi + +# Wait for the event socket to be ready. +while [[ ! -S "${sock}" ]]; do + sleep 0.2 +done + +nc -U "${sock}" | while read -r line; do + case "${line}" in + workspace*">>"*) + payload="${line#*>>}" + # Handle workspacev2 payloads: id,name + if [[ "${payload}" == *","* ]]; then + ws_id="${payload%%,*}" + ws_name="${payload#*,}" + else + ws_id="${payload}" + ws_name="${payload}" + fi + + # Ignore special/negative workspaces. + if [[ "${ws_id}" =~ ^- ]] || [[ "${ws_name}" == special:* ]]; then + continue + fi + + ws_ident="${ws_name}" + if [[ -z "${ws_ident}" ]]; then + ws_ident="${ws_id}" + fi + + prev="$(cat "${last_file}" 2>/dev/null || true)" + if [[ -n "${prev}" && "${ws_ident}" != "${prev}" ]]; then + echo "${prev}" > "${prev_file}" + fi + echo "${ws_ident}" > "${last_file}" + ;; + esac +done