Remove Hyprland shell script bindings
This commit is contained in:
@@ -26,8 +26,7 @@ $runMenu = rofi -show run
|
||||
# ENVIRONMENT VARIABLES
|
||||
# =============================================================================
|
||||
env = XCURSOR_SIZE,24
|
||||
env = QT_QPA_PLATFORMTHEME,qt5ct
|
||||
# Used by ~/.config/hypr/scripts/* to keep workspace IDs bounded.
|
||||
env = QT_QPA_PLATFORMTHEME,qt6ct
|
||||
env = HYPR_MAX_WORKSPACE,9
|
||||
|
||||
# =============================================================================
|
||||
@@ -260,8 +259,8 @@ bind = $mainMod SHIFT, Q, exit,
|
||||
bind = $mainMod, E, exec, emacsclient --eval '(emacs-everywhere)'
|
||||
bind = $mainMod, V, exec, wl-paste | xdotool type --file -
|
||||
|
||||
# Chrome/Browser (raise or spawn like XMonad's bindBringAndRaise)
|
||||
bind = $modAlt, C, exec, ~/.config/hypr/scripts/raise-or-run.sh google-chrome google-chrome-stable
|
||||
# Chrome/Browser
|
||||
bind = $modAlt, C, exec, google-chrome-stable
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# SCRATCHPADS (managed by hyprscratch daemon with auto-dismiss)
|
||||
@@ -291,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, exec, ~/.config/hypr/scripts/movewindow-follow-cursor.sh u once
|
||||
bind = $mainMod SHIFT, S, exec, ~/.config/hypr/scripts/movewindow-follow-cursor.sh d once
|
||||
bind = $mainMod SHIFT, A, exec, ~/.config/hypr/scripts/movewindow-follow-cursor.sh l once
|
||||
bind = $mainMod SHIFT, D, exec, ~/.config/hypr/scripts/movewindow-follow-cursor.sh r once
|
||||
# Move windows (Mod + Shift + WASD)
|
||||
bind = $mainMod SHIFT, W, hy3:movewindow, u
|
||||
bind = $mainMod SHIFT, S, hy3:movewindow, d
|
||||
bind = $mainMod SHIFT, A, hy3:movewindow, l
|
||||
bind = $mainMod SHIFT, D, hy3:movewindow, r
|
||||
|
||||
# Resize windows (Mod + Ctrl + WASD)
|
||||
binde = $mainMod CTRL, W, resizeactive, 0 -50
|
||||
@@ -315,13 +314,6 @@ bind = $hyper SHIFT, S, movewindow, mon:d
|
||||
bind = $hyper SHIFT, A, movewindow, mon:l
|
||||
bind = $hyper SHIFT, D, movewindow, mon:r
|
||||
|
||||
# Shift to empty workspace on screen direction (Super + Ctrl + Shift + WASD)
|
||||
# Like XMonad's shiftToEmptyOnScreen
|
||||
bind = $mainMod CTRL SHIFT, W, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh u
|
||||
bind = $mainMod CTRL SHIFT, S, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh d
|
||||
bind = $mainMod CTRL SHIFT, A, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh l
|
||||
bind = $mainMod CTRL SHIFT, D, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh r
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# LAYOUT CONTROL (XMonad-like with hy3)
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -373,31 +365,6 @@ bind = $mainMod, N, hy3:killactive
|
||||
# hy3:setswallow - set a window to swallow newly spawned windows
|
||||
bind = $mainMod CTRL, M, hy3:setswallow, toggle
|
||||
|
||||
# Minimize/unminimize (via special workspace)
|
||||
bind = $mainMod, M, exec, ~/.config/hypr/scripts/minimize-active.sh minimized
|
||||
bind = $mainMod SHIFT, M, exec, ~/.config/hypr/scripts/unminimize-last.sh minimized
|
||||
|
||||
# Minimized "picker" mode:
|
||||
# Open the minimized special workspace, focus a window, press Enter to restore it.
|
||||
bind = $modAlt, Return, exec, ~/.config/hypr/scripts/minimized-mode.sh minimized
|
||||
|
||||
submap = minimized
|
||||
bind = , Return, exec, ~/.config/hypr/scripts/unminimize-last.sh minimized; hyprctl dispatch submap reset
|
||||
bind = , Escape, exec, ~/.config/hypr/scripts/minimized-cancel.sh minimized
|
||||
bind = $modAlt, Return, exec, ~/.config/hypr/scripts/minimized-cancel.sh minimized
|
||||
|
||||
# Optional: basic focus navigation inside the picker.
|
||||
bind = , H, movefocus, l
|
||||
bind = , J, movefocus, d
|
||||
bind = , K, movefocus, u
|
||||
bind = , L, movefocus, r
|
||||
bind = , left, movefocus, l
|
||||
bind = , down, movefocus, d
|
||||
bind = , up, movefocus, u
|
||||
bind = , right, movefocus, r
|
||||
|
||||
submap = reset
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WORKSPACE CONTROL
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -448,38 +415,14 @@ bind = $mainMod CTRL, 9, focusworkspaceoncurrentmonitor, 9
|
||||
# built-in per-monitor workspace history.
|
||||
bind = $mainMod, backslash, workspace, previous_per_monitor
|
||||
|
||||
# Swap current workspace with another (like XMonad's swapWithCurrent)
|
||||
bind = $hyper, 5, exec, ~/.config/hypr/scripts/swap-workspaces.sh
|
||||
|
||||
# Go to next empty workspace (like XMonad's moveTo Next emptyWS)
|
||||
bind = $hyper, E, exec, ~/.config/hypr/scripts/workspace-goto-empty.sh
|
||||
|
||||
# Move to next screen (like XMonad's shiftToNextScreenX)
|
||||
bind = $mainMod, Z, focusmonitor, +1
|
||||
bind = $mainMod SHIFT, Z, movewindow, mon:+1
|
||||
|
||||
# Shift to empty workspace and view (like XMonad's shiftToEmptyAndView)
|
||||
bind = $mainMod SHIFT, H, exec, ~/.config/hypr/scripts/workspace-move-to-empty.sh
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WINDOW MANAGEMENT
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Go to window (rofi window switcher with icons)
|
||||
bind = $mainMod, G, exec, ~/.config/hypr/scripts/go-to-window.sh
|
||||
|
||||
# Bring window (move to current workspace)
|
||||
bind = $mainMod, B, exec, ~/.config/hypr/scripts/bring-window.sh
|
||||
|
||||
# Replace window (swap focused with selected - like XMonad's myReplaceWindow)
|
||||
bind = $mainMod SHIFT, B, exec, ~/.config/hypr/scripts/replace-window.sh
|
||||
|
||||
# Gather windows of same class (like XMonad's gatherThisClass)
|
||||
bind = $hyper, G, exec, ~/.config/hypr/scripts/gather-class.sh
|
||||
|
||||
# Focus next window of different class (like XMonad's focusNextClass)
|
||||
bind = $mainMod, apostrophe, exec, ~/.config/hypr/scripts/focus-next-class.sh
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# MEDIA KEYS
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -541,8 +484,8 @@ bindm = $mainMod, mouse:272, movewindow
|
||||
bindm = $mainMod, mouse:273, resizewindow
|
||||
|
||||
# Scroll through workspaces
|
||||
bind = $mainMod, mouse_down, exec, ~/.config/hypr/scripts/workspace-scroll.sh +1
|
||||
bind = $mainMod, mouse_up, exec, ~/.config/hypr/scripts/workspace-scroll.sh -1
|
||||
bind = $mainMod, mouse_down, workspace, e+1
|
||||
bind = $mainMod, mouse_up, workspace, e-1
|
||||
|
||||
# =============================================================================
|
||||
# AUTOSTART
|
||||
|
||||
@@ -12,6 +12,9 @@ local columns_layout = "nStack"
|
||||
local monocle_layout = "monocle"
|
||||
local minimized_workspace = "special:minimized"
|
||||
local current_layout = columns_layout
|
||||
local enable_nstack = true
|
||||
local enable_hyprexpo = true
|
||||
local configure_nstack_plugin_from_lua = false
|
||||
local workspace_layouts = {}
|
||||
local minimized_windows = {}
|
||||
local window_picker_mode = nil
|
||||
@@ -101,7 +104,7 @@ local function hyprexpo(action)
|
||||
end
|
||||
|
||||
local function apply_nstack_config()
|
||||
if verify_config then
|
||||
if verify_config or not enable_nstack or not configure_nstack_plugin_from_lua then
|
||||
return
|
||||
end
|
||||
|
||||
@@ -126,7 +129,7 @@ local function apply_nstack_config()
|
||||
end
|
||||
|
||||
local function apply_hyprexpo_config()
|
||||
if verify_config then
|
||||
if verify_config or not enable_hyprexpo then
|
||||
return
|
||||
end
|
||||
|
||||
@@ -294,7 +297,7 @@ local function find_empty_workspace(target_monitor, exclude_id)
|
||||
end
|
||||
|
||||
local function update_nstack_count()
|
||||
if current_layout ~= columns_layout then
|
||||
if not enable_nstack or current_layout ~= columns_layout then
|
||||
return
|
||||
end
|
||||
|
||||
@@ -303,6 +306,7 @@ local function update_nstack_count()
|
||||
if count == 0 then
|
||||
return
|
||||
end
|
||||
count = math.max(count, 2)
|
||||
hl.dsp.layout("setstackcount " .. tostring(count))()
|
||||
end
|
||||
|
||||
@@ -1103,8 +1107,10 @@ local function toggle_scratchpad(name)
|
||||
end
|
||||
end
|
||||
|
||||
if enable_nstack then
|
||||
hl.plugin.load("/run/current-system/sw/lib/libhyprNStack.so")
|
||||
if not verify_config then
|
||||
end
|
||||
if enable_hyprexpo and not verify_config then
|
||||
hl.plugin.load("/run/current-system/sw/lib/libhyprexpo.so")
|
||||
end
|
||||
|
||||
@@ -1185,6 +1191,7 @@ hl.curve("smoothOut", { type = "bezier", points = { { 0.36, 1 }, { 0.3, 1 } } })
|
||||
hl.curve("smoothInOut", { type = "bezier", points = { { 0.42, 0 }, { 0.58, 1 } } })
|
||||
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
|
||||
|
||||
hl.animation({ leaf = "global", enabled = true, speed = 8, bezier = "default" })
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 6, bezier = "overshoot", style = "gnomed" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 6, bezier = "overshoot", style = "gnomed" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 5, bezier = "smoothInOut", style = "gnomed" })
|
||||
@@ -1226,8 +1233,6 @@ local function apply_rules()
|
||||
})
|
||||
end
|
||||
|
||||
apply_rules()
|
||||
|
||||
bind(main_mod .. " + P", exec(menu))
|
||||
bind(main_mod .. " + SHIFT + P", exec(run_menu))
|
||||
bind(main_mod .. " + SHIFT + Return", exec(terminal))
|
||||
@@ -1465,6 +1470,7 @@ bind(main_mod .. " + mouse:273", hl.dsp.window.resize())
|
||||
hl.on("hyprland.start", function()
|
||||
apply_nstack_config()
|
||||
apply_hyprexpo_config()
|
||||
apply_rules()
|
||||
hl.exec_cmd("sh -lc 'export IMALISON_SESSION_TYPE=wayland; dbus-update-activation-environment --systemd WAYLAND_DISPLAY DISPLAY XAUTHORITY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP XDG_SESSION_TYPE IMALISON_SESSION_TYPE; systemctl --user start graphical-session.target hyprland-session.target'")
|
||||
hl.exec_cmd("hypridle")
|
||||
hl.exec_cmd("wl-paste --type text --watch cliphist store")
|
||||
@@ -1476,6 +1482,7 @@ end)
|
||||
|
||||
hl.on("config.reloaded", apply_nstack_config)
|
||||
hl.on("config.reloaded", apply_hyprexpo_config)
|
||||
hl.on("config.reloaded", apply_rules)
|
||||
|
||||
hl.on("window.open", schedule_nstack_count_update)
|
||||
hl.on("window.destroy", schedule_nstack_count_update)
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Bring window to current workspace (like XMonad's bringWindow)
|
||||
# Uses rofi with icons to select a window, then moves it here.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
source "$SCRIPT_DIR/window-icon-map.sh"
|
||||
|
||||
CURRENT_WS=$(hyprctl activeworkspace -j | jq -r '.id')
|
||||
|
||||
# Get windows on OTHER workspaces as TSV
|
||||
WINDOW_DATA=$(hyprctl clients -j | jq -r --argjson cws "$CURRENT_WS" '
|
||||
.[] | select(.workspace.id >= 0 and .workspace.id != $cws)
|
||||
| [.address, .class, (.title | gsub("\t"; " ")), (.workspace.id | tostring)]
|
||||
| @tsv')
|
||||
|
||||
if [ -z "$WINDOW_DATA" ]; then
|
||||
notify-send "Bring Window" "No windows on other workspaces"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
addresses=()
|
||||
TMPFILE=$(mktemp)
|
||||
trap 'rm -f "$TMPFILE"' EXIT
|
||||
|
||||
while IFS=$'\t' read -r address class title ws_id; do
|
||||
icon=$(icon_for_class "$class")
|
||||
addresses+=("$address")
|
||||
printf '%-24s %s WS:%s\0icon\x1f%s\n' \
|
||||
"$class" "$title" "$ws_id" "$icon"
|
||||
done <<< "$WINDOW_DATA" > "$TMPFILE"
|
||||
|
||||
INDEX=$(rofi -dmenu -i -show-icons -p "Bring window" -format i < "$TMPFILE") || exit 0
|
||||
|
||||
if [ -n "$INDEX" ] && [ -n "${addresses[$INDEX]:-}" ]; then
|
||||
ADDRESS="${addresses[$INDEX]}"
|
||||
hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$ADDRESS"
|
||||
hyprctl dispatch focuswindow "address:$ADDRESS"
|
||||
fi
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Cycle between master and dwindle layouts
|
||||
# Like XMonad's NextLayout
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CURRENT=$(hyprctl getoption general:layout -j | jq -r '.str')
|
||||
|
||||
if [ "$CURRENT" = "master" ]; then
|
||||
hyprctl keyword general:layout dwindle
|
||||
notify-send "Layout" "Switched to Dwindle (binary tree)"
|
||||
else
|
||||
hyprctl keyword general:layout master
|
||||
notify-send "Layout" "Switched to Master (XMonad-like)"
|
||||
fi
|
||||
@@ -1,72 +0,0 @@
|
||||
#!/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
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Focus next window of a different class (like XMonad's focusNextClass)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Get focused window class
|
||||
FOCUSED_CLASS=$(hyprctl activewindow -j | jq -r '.class')
|
||||
FOCUSED_ADDR=$(hyprctl activewindow -j | jq -r '.address')
|
||||
|
||||
if [ "$FOCUSED_CLASS" = "null" ] || [ -z "$FOCUSED_CLASS" ]; then
|
||||
# No focused window, just focus any window
|
||||
hyprctl dispatch cyclenext
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get all unique classes
|
||||
ALL_CLASSES=$(hyprctl clients -j | jq -r '[.[] | select(.workspace.id >= 0) | .class] | unique | .[]')
|
||||
|
||||
# Get sorted list of classes
|
||||
CLASSES_ARRAY=()
|
||||
while IFS= read -r class; do
|
||||
CLASSES_ARRAY+=("$class")
|
||||
done <<< "$ALL_CLASSES"
|
||||
|
||||
# Find current class index and get next class
|
||||
CURRENT_INDEX=-1
|
||||
for i in "${!CLASSES_ARRAY[@]}"; do
|
||||
if [ "${CLASSES_ARRAY[$i]}" = "$FOCUSED_CLASS" ]; then
|
||||
CURRENT_INDEX=$i
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $CURRENT_INDEX -eq -1 ] || [ ${#CLASSES_ARRAY[@]} -le 1 ]; then
|
||||
# Only one class or class not found
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get next class (wrapping around)
|
||||
NEXT_INDEX=$(( (CURRENT_INDEX + 1) % ${#CLASSES_ARRAY[@]} ))
|
||||
NEXT_CLASS="${CLASSES_ARRAY[$NEXT_INDEX]}"
|
||||
|
||||
# Find first window of next class
|
||||
NEXT_WINDOW=$(hyprctl clients -j | jq -r ".[] | select(.class == \"$NEXT_CLASS\" and .workspace.id >= 0) | .address" | head -1)
|
||||
|
||||
if [ -n "$NEXT_WINDOW" ]; then
|
||||
hyprctl dispatch focuswindow "address:$NEXT_WINDOW"
|
||||
fi
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Gather all windows of the same class as focused window (like XMonad's gatherThisClass)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Get focused window class
|
||||
FOCUSED_CLASS=$(hyprctl activewindow -j | jq -r '.class')
|
||||
CURRENT_WS=$(hyprctl activeworkspace -j | jq -r '.id')
|
||||
|
||||
if [ "$FOCUSED_CLASS" = "null" ] || [ -z "$FOCUSED_CLASS" ]; then
|
||||
notify-send "Gather Class" "No focused window"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Find all windows with same class on other workspaces
|
||||
WINDOWS=$(hyprctl clients -j | jq -r ".[] | select(.class == \"$FOCUSED_CLASS\" and .workspace.id != $CURRENT_WS and .workspace.id >= 0) | .address")
|
||||
|
||||
if [ -z "$WINDOWS" ]; then
|
||||
notify-send "Gather Class" "No other windows of class '$FOCUSED_CLASS'"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Move each window to current workspace
|
||||
COUNT=0
|
||||
for ADDR in $WINDOWS; do
|
||||
hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$ADDR"
|
||||
COUNT=$((COUNT + 1))
|
||||
done
|
||||
|
||||
notify-send "Gather Class" "Gathered $COUNT windows of class '$FOCUSED_CLASS'"
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Go to a window selected via rofi (with icons from desktop entries).
|
||||
# Replaces "rofi -show window" which doesn't work well on Wayland.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
source "$SCRIPT_DIR/window-icon-map.sh"
|
||||
|
||||
# Get all windows on regular workspaces as TSV
|
||||
WINDOW_DATA=$(hyprctl clients -j | jq -r '
|
||||
.[] | select(.workspace.id >= 0)
|
||||
| [.address, .class, (.title | gsub("\t"; " ")), (.workspace.id | tostring)]
|
||||
| @tsv')
|
||||
|
||||
[ -n "$WINDOW_DATA" ] || exit 0
|
||||
|
||||
addresses=()
|
||||
TMPFILE=$(mktemp)
|
||||
trap 'rm -f "$TMPFILE"' EXIT
|
||||
|
||||
while IFS=$'\t' read -r address class title ws_id; do
|
||||
icon=$(icon_for_class "$class")
|
||||
addresses+=("$address")
|
||||
printf '%-24s %s WS:%s\0icon\x1f%s\n' \
|
||||
"$class" "$title" "$ws_id" "$icon"
|
||||
done <<< "$WINDOW_DATA" > "$TMPFILE"
|
||||
|
||||
INDEX=$(rofi -dmenu -i -show-icons -p "Go to window" -format i < "$TMPFILE") || exit 0
|
||||
|
||||
if [ -n "$INDEX" ] && [ -n "${addresses[$INDEX]:-}" ]; then
|
||||
hyprctl dispatch focuswindow "address:${addresses[$INDEX]}"
|
||||
fi
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Minimize the active window by moving it to a special workspace without
|
||||
# toggling that special workspace open.
|
||||
#
|
||||
# Usage: minimize-active.sh <name>
|
||||
# Example: minimize-active.sh minimized
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NAME="${1:-minimized}"
|
||||
NAME="${NAME#special:}"
|
||||
|
||||
if ! command -v hyprctl >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
# We could parse plain output, but jq should exist in this setup; if it
|
||||
# doesn't, fail soft.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ACTIVE_JSON="$(hyprctl -j activewindow 2>/dev/null || true)"
|
||||
ADDR="$(printf '%s' "$ACTIVE_JSON" | jq -r '.address // empty')"
|
||||
if [ -z "$ADDR" ] || [ "$ADDR" = "null" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If the minimized special workspace is currently visible, closing it after the
|
||||
# move keeps the window hidden (what "minimize" usually means).
|
||||
MONITOR_ID="$(printf '%s' "$ACTIVE_JSON" | jq -r '.monitor // empty')"
|
||||
SPECIAL_OPEN="$(
|
||||
hyprctl -j monitors 2>/dev/null \
|
||||
| jq -r --arg n "special:$NAME" --argjson mid "${MONITOR_ID:-0}" '
|
||||
.[]
|
||||
| select(.id == $mid)
|
||||
| (.specialWorkspace.name // "")
|
||||
| select(. == $n)
|
||||
' \
|
||||
| head -n 1 \
|
||||
|| true
|
||||
)"
|
||||
|
||||
hyprctl dispatch movetoworkspacesilent "special:${NAME},address:${ADDR}" >/dev/null 2>&1 || true
|
||||
|
||||
if [ -n "$SPECIAL_OPEN" ]; then
|
||||
hyprctl dispatch togglespecialworkspace "$NAME" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Exit minimized picker mode:
|
||||
# - Hide the minimized special workspace on the active monitor (if visible)
|
||||
# - Reset the submap
|
||||
#
|
||||
# Usage: minimized-cancel.sh <name>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NAME="${1:-minimized}"
|
||||
NAME="${NAME#special:}"
|
||||
SPECIAL_WS="special:${NAME}"
|
||||
|
||||
if ! command -v hyprctl >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
MONITOR_ID="$(hyprctl -j activeworkspace 2>/dev/null | jq -r '.monitorID // empty' || true)"
|
||||
if [ -z "$MONITOR_ID" ] || [ "$MONITOR_ID" = "null" ]; then
|
||||
MONITOR_ID=0
|
||||
fi
|
||||
|
||||
OPEN="$(
|
||||
hyprctl -j monitors 2>/dev/null \
|
||||
| jq -r --argjson mid "$MONITOR_ID" '.[] | select(.id == $mid) | (.specialWorkspace.name // "")' \
|
||||
| head -n 1 \
|
||||
|| true
|
||||
)"
|
||||
|
||||
if [ "$OPEN" = "$SPECIAL_WS" ]; then
|
||||
hyprctl dispatch togglespecialworkspace "$NAME" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
hyprctl dispatch submap reset >/dev/null 2>&1 || true
|
||||
exit 0
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Enter a "picker" mode for minimized windows:
|
||||
# - Ensure the minimized special workspace is visible on the active monitor
|
||||
# - Switch Hyprland into a submap so Enter restores and Escape cancels
|
||||
#
|
||||
# Usage: minimized-mode.sh <name>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NAME="${1:-minimized}"
|
||||
NAME="${NAME#special:}"
|
||||
SPECIAL_WS="special:${NAME}"
|
||||
|
||||
if ! command -v hyprctl >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
MONITOR_ID="$(hyprctl -j activeworkspace 2>/dev/null | jq -r '.monitorID // empty' || true)"
|
||||
if [ -z "$MONITOR_ID" ] || [ "$MONITOR_ID" = "null" ]; then
|
||||
MONITOR_ID=0
|
||||
fi
|
||||
|
||||
OPEN="$(
|
||||
hyprctl -j monitors 2>/dev/null \
|
||||
| jq -r --argjson mid "$MONITOR_ID" '.[] | select(.id == $mid) | (.specialWorkspace.name // "")' \
|
||||
| head -n 1 \
|
||||
|| true
|
||||
)"
|
||||
|
||||
# Ensure it's visible (but don't toggle it off if already open).
|
||||
if [ "$OPEN" != "$SPECIAL_WS" ]; then
|
||||
hyprctl dispatch togglespecialworkspace "$NAME" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
hyprctl dispatch submap minimized >/dev/null 2>&1 || true
|
||||
exit 0
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Move the active window in a direction and warp the cursor to keep its
|
||||
# relative position inside the moved window.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
export PATH="/run/current-system/sw/bin:${PATH}"
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "usage: $0 <dir> [mode]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dir="$1"
|
||||
mode="${2:-}"
|
||||
|
||||
if ! command -v hyprctl >/dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
move_window() {
|
||||
if [[ -n "$mode" ]]; then
|
||||
hyprctl dispatch hy3:movewindow "$dir, $mode" >/dev/null 2>&1 || true
|
||||
else
|
||||
hyprctl dispatch hy3:movewindow "$dir" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
win_json="$(hyprctl -j activewindow 2>/dev/null || true)"
|
||||
cur_json="$(hyprctl -j cursorpos 2>/dev/null || true)"
|
||||
|
||||
if [[ -z "$win_json" || "$win_json" == "null" || -z "$cur_json" || "$cur_json" == "null" ]]; then
|
||||
move_window
|
||||
exit 0
|
||||
fi
|
||||
|
||||
win_x="$(jq -er '.at[0]' <<<"$win_json" 2>/dev/null || true)"
|
||||
win_y="$(jq -er '.at[1]' <<<"$win_json" 2>/dev/null || true)"
|
||||
win_w="$(jq -er '.size[0]' <<<"$win_json" 2>/dev/null || true)"
|
||||
win_h="$(jq -er '.size[1]' <<<"$win_json" 2>/dev/null || true)"
|
||||
cur_x="$(jq -er '.x' <<<"$cur_json" 2>/dev/null || true)"
|
||||
cur_y="$(jq -er '.y' <<<"$cur_json" 2>/dev/null || true)"
|
||||
|
||||
if [[ ! "$win_x" =~ ^-?[0-9]+$ || ! "$win_y" =~ ^-?[0-9]+$ || ! "$win_w" =~ ^-?[0-9]+$ || ! "$win_h" =~ ^-?[0-9]+$ || ! "$cur_x" =~ ^-?[0-9]+$ || ! "$cur_y" =~ ^-?[0-9]+$ ]]; then
|
||||
move_window
|
||||
exit 0
|
||||
fi
|
||||
|
||||
rel_x=$((cur_x - win_x))
|
||||
rel_y=$((cur_y - win_y))
|
||||
|
||||
move_window
|
||||
|
||||
win_json="$(hyprctl -j activewindow 2>/dev/null || true)"
|
||||
if [[ -z "$win_json" || "$win_json" == "null" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
win_x="$(jq -er '.at[0]' <<<"$win_json" 2>/dev/null || true)"
|
||||
win_y="$(jq -er '.at[1]' <<<"$win_json" 2>/dev/null || true)"
|
||||
win_w="$(jq -er '.size[0]' <<<"$win_json" 2>/dev/null || true)"
|
||||
win_h="$(jq -er '.size[1]' <<<"$win_json" 2>/dev/null || true)"
|
||||
|
||||
if [[ ! "$win_x" =~ ^-?[0-9]+$ || ! "$win_y" =~ ^-?[0-9]+$ || ! "$win_w" =~ ^-?[0-9]+$ || ! "$win_h" =~ ^-?[0-9]+$ ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ((rel_x < 0)); then
|
||||
rel_x=0
|
||||
elif ((rel_x > win_w)); then
|
||||
rel_x=$win_w
|
||||
fi
|
||||
|
||||
if ((rel_y < 0)); then
|
||||
rel_y=0
|
||||
elif ((rel_y > win_h)); then
|
||||
rel_y=$win_h
|
||||
fi
|
||||
|
||||
new_x=$((win_x + rel_x))
|
||||
new_y=$((win_y + rel_y))
|
||||
|
||||
hyprctl dispatch movecursor "$new_x" "$new_y" >/dev/null 2>&1 || true
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Raise existing window or run command (like XMonad's raiseNextMaybe)
|
||||
# Usage: raise-or-run.sh <class-pattern> <command>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CLASS_PATTERN="$1"
|
||||
COMMAND="$2"
|
||||
|
||||
# Find windows matching the class pattern
|
||||
MATCHING=$(hyprctl clients -j | jq -r ".[] | select(.class | test(\"$CLASS_PATTERN\"; \"i\")) | .address" | head -1)
|
||||
|
||||
if [ -n "$MATCHING" ]; then
|
||||
# Window exists, focus it
|
||||
hyprctl dispatch focuswindow "address:$MATCHING"
|
||||
else
|
||||
# No matching window, run the command
|
||||
exec $COMMAND
|
||||
fi
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Replace focused window with selected window (like XMonad's myReplaceWindow)
|
||||
# Swaps the positions of focused window and selected window
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
source "$SCRIPT_DIR/window-icon-map.sh"
|
||||
|
||||
FOCUSED=$(hyprctl activewindow -j | jq -r '.address')
|
||||
|
||||
if [ "$FOCUSED" = "null" ] || [ -z "$FOCUSED" ]; then
|
||||
notify-send "Replace Window" "No focused window"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get all windows except focused as TSV
|
||||
WINDOW_DATA=$(hyprctl clients -j | jq -r --arg focused "$FOCUSED" '
|
||||
.[] | select(.workspace.id >= 0 and .address != $focused)
|
||||
| [.address, .class, (.title | gsub("\t"; " ")), (.workspace.id | tostring)]
|
||||
| @tsv')
|
||||
|
||||
if [ -z "$WINDOW_DATA" ]; then
|
||||
notify-send "Replace Window" "No other windows available"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
addresses=()
|
||||
TMPFILE=$(mktemp)
|
||||
trap 'rm -f "$TMPFILE"' EXIT
|
||||
|
||||
while IFS=$'\t' read -r address class title ws_id; do
|
||||
icon=$(icon_for_class "$class")
|
||||
addresses+=("$address")
|
||||
printf '%-24s %s WS:%s\0icon\x1f%s\n' \
|
||||
"$class" "$title" "$ws_id" "$icon"
|
||||
done <<< "$WINDOW_DATA" > "$TMPFILE"
|
||||
|
||||
INDEX=$(rofi -dmenu -i -show-icons -p "Replace with" -format i < "$TMPFILE") || exit 0
|
||||
|
||||
if [ -n "$INDEX" ] && [ -n "${addresses[$INDEX]:-}" ]; then
|
||||
hyprctl dispatch hy3:movewindow "address:${addresses[$INDEX]}"
|
||||
fi
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shift window to empty workspace on screen in given direction
|
||||
# Like XMonad's shiftToEmptyOnScreen
|
||||
# Usage: shift-to-empty-on-screen.sh <direction: u|d|l|r>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DIRECTION="$1"
|
||||
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||
|
||||
# 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 (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 within 1..$HYPR_MAX_WORKSPACE.
|
||||
EMPTY_WS="$(~/.config/hypr/scripts/find-empty-workspace.sh "${MONITOR}" 2>/dev/null || true)"
|
||||
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 (( EMPTY_WS < 1 || EMPTY_WS > max_ws )); then
|
||||
hyprctl dispatch focusmonitor "$ORIG_MONITOR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Ensure the workspace exists on the target monitor
|
||||
hyprctl dispatch workspace "$EMPTY_WS"
|
||||
|
||||
# Go back to original monitor and move the window (without following)
|
||||
hyprctl dispatch focusmonitor "$ORIG_MONITOR"
|
||||
hyprctl dispatch movetoworkspacesilent "$EMPTY_WS"
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Swap the contents of the current workspace with another workspace.
|
||||
# Intended to mirror XMonad's swapWithCurrent behavior.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||
|
||||
CURRENT_WS="$(hyprctl activeworkspace -j | jq -r '.id')"
|
||||
if [[ -z "${CURRENT_WS}" || "${CURRENT_WS}" == "null" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TARGET_WS="${1:-}"
|
||||
|
||||
if [[ -z "${TARGET_WS}" ]]; then
|
||||
WS_LIST="$({
|
||||
seq 1 "${max_ws}"
|
||||
hyprctl workspaces -j | jq -r '.[].id' 2>/dev/null || true
|
||||
} | awk 'NF {print $1}' | awk '!seen[$0]++' | sort -n)"
|
||||
|
||||
TARGET_WS="$(printf "%s\n" "${WS_LIST}" | rofi -dmenu -p "Swap with workspace")"
|
||||
fi
|
||||
|
||||
if [[ -z "${TARGET_WS}" || "${TARGET_WS}" == "null" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${TARGET_WS}" == "${CURRENT_WS}" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! [[ "${TARGET_WS}" =~ ^-?[0-9]+$ ]]; then
|
||||
notify-send "Swap Workspace" "Invalid workspace: ${TARGET_WS}"
|
||||
exit 1
|
||||
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_TARGET="$(hyprctl clients -j | jq -r --arg ws "${TARGET_WS}" '.[] | select((.workspace.id|tostring) == $ws) | .address')"
|
||||
|
||||
for ADDR in ${WINDOWS_CURRENT}; do
|
||||
hyprctl dispatch movetoworkspace "${TARGET_WS},address:${ADDR}"
|
||||
done
|
||||
|
||||
for ADDR in ${WINDOWS_TARGET}; do
|
||||
hyprctl dispatch movetoworkspace "${CURRENT_WS},address:${ADDR}"
|
||||
done
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Toggle a named Hyprland scratchpad, spawning it if needed.
|
||||
# Usage: toggle-scratchpad.sh <name> <class_regex|-> <title_regex|-> <command...>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -lt 4 ]; then
|
||||
echo "usage: $0 <name> <class_regex|-> <title_regex|-> <command...>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NAME="$1"
|
||||
shift
|
||||
CLASS_REGEX="$1"
|
||||
shift
|
||||
TITLE_REGEX="$1"
|
||||
shift
|
||||
COMMAND=("$@")
|
||||
|
||||
if [ "$CLASS_REGEX" = "-" ]; then
|
||||
CLASS_REGEX=""
|
||||
fi
|
||||
if [ "$TITLE_REGEX" = "-" ]; then
|
||||
TITLE_REGEX=""
|
||||
fi
|
||||
|
||||
if [ -z "$CLASS_REGEX" ] && [ -z "$TITLE_REGEX" ]; then
|
||||
echo "toggle-scratchpad: provide a class or title regex" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MATCHING=$(hyprctl clients -j | jq -r --arg cre "$CLASS_REGEX" --arg tre "$TITLE_REGEX" '
|
||||
.[]
|
||||
| select(
|
||||
(($cre == "") or (.class | test($cre; "i")))
|
||||
and
|
||||
(($tre == "") or (.title | test($tre; "i")))
|
||||
)
|
||||
| .address
|
||||
')
|
||||
|
||||
if [ -z "$MATCHING" ]; then
|
||||
"${COMMAND[@]}" &
|
||||
else
|
||||
while IFS= read -r ADDR; do
|
||||
[ -n "$ADDR" ] || continue
|
||||
hyprctl dispatch movetoworkspacesilent "special:$NAME,address:$ADDR"
|
||||
done <<< "$MATCHING"
|
||||
fi
|
||||
|
||||
hyprctl dispatch togglespecialworkspace "$NAME"
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Restore a minimized window by moving it out of a special workspace.
|
||||
#
|
||||
# Usage: unminimize-last.sh <name>
|
||||
# Example: unminimize-last.sh minimized
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NAME="${1:-minimized}"
|
||||
NAME="${NAME#special:}"
|
||||
SPECIAL_WS="special:${NAME}"
|
||||
|
||||
if ! command -v hyprctl >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ACTIVE_JSON="$(hyprctl -j activewindow 2>/dev/null || true)"
|
||||
ACTIVE_ADDR="$(printf '%s' "$ACTIVE_JSON" | jq -r '.address // empty')"
|
||||
ACTIVE_WS="$(printf '%s' "$ACTIVE_JSON" | jq -r '.workspace.name // empty')"
|
||||
MONITOR_ID="$(printf '%s' "$ACTIVE_JSON" | jq -r '.monitor // empty')"
|
||||
|
||||
# Destination is the normal active workspace for the active monitor.
|
||||
DEST_WS="$(
|
||||
hyprctl -j monitors 2>/dev/null \
|
||||
| jq -r --argjson mid "${MONITOR_ID:-0}" '.[] | select(.id == $mid) | .activeWorkspace.name' \
|
||||
| head -n 1 \
|
||||
|| true
|
||||
)"
|
||||
if [ -z "$DEST_WS" ] || [ "$DEST_WS" = "null" ]; then
|
||||
DEST_WS="$(hyprctl -j activeworkspace 2>/dev/null | jq -r '.name // empty' || true)"
|
||||
fi
|
||||
if [ -z "$DEST_WS" ] || [ "$DEST_WS" = "null" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If we're focused on a minimized window already, restore that one.
|
||||
ADDR=""
|
||||
if [ "$ACTIVE_WS" = "$SPECIAL_WS" ] && [ -n "$ACTIVE_ADDR" ] && [ "$ACTIVE_ADDR" != "null" ]; then
|
||||
ADDR="$ACTIVE_ADDR"
|
||||
else
|
||||
# Otherwise, restore the "most recent" minimized window we can find.
|
||||
# focusHistoryID tends to have 0 as most recent; pick the smallest value.
|
||||
ADDR="$(
|
||||
hyprctl -j clients 2>/dev/null \
|
||||
| jq -r --arg sw "$SPECIAL_WS" '
|
||||
[ .[]
|
||||
| select(.workspace.name == $sw)
|
||||
| { addr: .address, fh: (.focusHistoryID // 999999999) }
|
||||
]
|
||||
| sort_by(.fh)
|
||||
| (.[0].addr // empty)
|
||||
' \
|
||||
| head -n 1 \
|
||||
|| true
|
||||
)"
|
||||
fi
|
||||
|
||||
if [ -z "$ADDR" ] || [ "$ADDR" = "null" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
hyprctl dispatch movetoworkspacesilent "${DEST_WS},address:${ADDR}" >/dev/null 2>&1 || true
|
||||
hyprctl dispatch focuswindow "address:${ADDR}" >/dev/null 2>&1 || true
|
||||
|
||||
# If the minimized special workspace is currently visible, close it so we don't
|
||||
# leave things in a special state after a restore.
|
||||
SPECIAL_OPEN="$(
|
||||
hyprctl -j monitors 2>/dev/null \
|
||||
| jq -r --arg n "$SPECIAL_WS" --argjson mid "${MONITOR_ID:-0}" '
|
||||
.[]
|
||||
| select(.id == $mid)
|
||||
| (.specialWorkspace.name // "")
|
||||
| select(. == $n)
|
||||
' \
|
||||
| head -n 1 \
|
||||
|| true
|
||||
)"
|
||||
if [ -n "$SPECIAL_OPEN" ]; then
|
||||
hyprctl dispatch togglespecialworkspace "$NAME" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Source this file to get icon_for_class function.
|
||||
# Builds a mapping from window class → freedesktop icon name
|
||||
# by scanning .desktop files for StartupWMClass and Icon fields.
|
||||
#
|
||||
# Usage:
|
||||
# source "$(dirname "$0")/window-icon-map.sh"
|
||||
# icon=$(icon_for_class "google-chrome")
|
||||
|
||||
declare -A _WINDOW_ICON_MAP
|
||||
|
||||
_build_window_icon_map() {
|
||||
local IFS=':'
|
||||
local -a search_dirs=()
|
||||
local dir
|
||||
|
||||
for dir in ${XDG_DATA_DIRS:-/run/current-system/sw/share:/usr/share:/usr/local/share}; do
|
||||
[ -d "$dir/applications" ] && search_dirs+=("$dir/applications")
|
||||
done
|
||||
[ -d "$HOME/.local/share/applications" ] && search_dirs+=("$HOME/.local/share/applications")
|
||||
[ ${#search_dirs[@]} -eq 0 ] && return
|
||||
|
||||
# Expand globs per-directory so the pattern works correctly
|
||||
local -a desktop_files=()
|
||||
for dir in "${search_dirs[@]}"; do
|
||||
desktop_files+=("$dir"/*.desktop)
|
||||
done
|
||||
[ ${#desktop_files[@]} -eq 0 ] && return
|
||||
|
||||
# Single grep pass across all desktop files
|
||||
local -A file_icons file_wmclass
|
||||
local filepath line
|
||||
while IFS=: read -r filepath line; do
|
||||
case "$line" in
|
||||
Icon=*)
|
||||
[ -z "${file_icons[$filepath]:-}" ] && file_icons["$filepath"]="${line#Icon=}"
|
||||
;;
|
||||
StartupWMClass=*)
|
||||
[ -z "${file_wmclass[$filepath]:-}" ] && file_wmclass["$filepath"]="${line#StartupWMClass=}"
|
||||
;;
|
||||
esac
|
||||
done < <(grep -H '^Icon=\|^StartupWMClass=' "${desktop_files[@]}" 2>/dev/null)
|
||||
|
||||
# Build class → icon map
|
||||
local icon wm_class bn name
|
||||
for filepath in "${!file_icons[@]}"; do
|
||||
icon="${file_icons[$filepath]}"
|
||||
[ -n "$icon" ] || continue
|
||||
|
||||
wm_class="${file_wmclass[$filepath]:-}"
|
||||
if [ -n "$wm_class" ]; then
|
||||
_WINDOW_ICON_MAP["${wm_class,,}"]="$icon"
|
||||
fi
|
||||
|
||||
bn="${filepath##*/}"
|
||||
name="${bn%.desktop}"
|
||||
_WINDOW_ICON_MAP["${name,,}"]="$icon"
|
||||
done
|
||||
}
|
||||
|
||||
_build_window_icon_map
|
||||
|
||||
icon_for_class() {
|
||||
local class_lower="${1,,}"
|
||||
echo "${_WINDOW_ICON_MAP[$class_lower]:-$class_lower}"
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/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,16 +0,0 @@
|
||||
#!/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
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/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
|
||||
|
||||
233
nixos/flake.lock
generated
233
nixos/flake.lock
generated
@@ -335,15 +335,15 @@
|
||||
"flake-compat_3": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "NixOS",
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -353,13 +353,13 @@
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "edolstra",
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -367,11 +367,11 @@
|
||||
"flake-compat_5": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -502,6 +502,24 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-blame-rank": {
|
||||
"inputs": {
|
||||
"fenix": "fenix",
|
||||
@@ -667,6 +685,7 @@
|
||||
"gitignore_3": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"imalison-taffybar",
|
||||
"taffybar",
|
||||
"weeder-nix",
|
||||
"pre-commit-hooks",
|
||||
@@ -712,7 +731,7 @@
|
||||
"hercules-ci-effects_2": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_5",
|
||||
"nixpkgs": "nixpkgs_6"
|
||||
"nixpkgs": "nixpkgs_7"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701009247,
|
||||
@@ -780,11 +799,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777317717,
|
||||
"narHash": "sha256-Rj4vx0RvEWtnpnizggWRtrGe092bXiGLLt0WijwYWtI=",
|
||||
"lastModified": 1777471960,
|
||||
"narHash": "sha256-X+xWT3VwjCRAxlBLZvBi5Lam4BpZk4k8ep7Fv13QNLw=",
|
||||
"owner": "colonelpanic8",
|
||||
"repo": "hyprNStack",
|
||||
"rev": "94607cd53f2ddac88f6b26261393275e7dd590ef",
|
||||
"rev": "d1b4307c0804ad22a17882bd5c5c2f5a7df2b3ea",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1084,16 +1103,16 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767020608,
|
||||
"narHash": "sha256-BSRT1Uu1ot4WfMfZc6KW0nwpmt2xl9wpUqmH/JoMTfk=",
|
||||
"owner": "hyprwm",
|
||||
"lastModified": 1777471981,
|
||||
"narHash": "sha256-cd3pQg+vKv6vht4xzButsi/Kaw9P4d3itm46jYXyiDM=",
|
||||
"owner": "colonelpanic8",
|
||||
"repo": "hyprland-plugins",
|
||||
"rev": "d7b67e8f4ba8ebeee4ce899348fcee6291512169",
|
||||
"rev": "725e354bbee982566068b5b90fec4fcd787c1036",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"ref": "v0.53.0",
|
||||
"owner": "colonelpanic8",
|
||||
"ref": "hyprexpo-v0.53.0-custom",
|
||||
"repo": "hyprland-plugins",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -1115,11 +1134,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777413654,
|
||||
"narHash": "sha256-lVGYGUWf9ynV5lR8QAygAfmYRkko1btIe26UcQ1bGXw=",
|
||||
"lastModified": 1777471960,
|
||||
"narHash": "sha256-Fr09SZhviCOxyuZiWku1X/rNXNvKrIqpsSdX1rQH+4o=",
|
||||
"owner": "colonelpanic8",
|
||||
"repo": "hyprland-plugins",
|
||||
"rev": "ff36c04b270c26fcd53e623fc688e4eb41672897",
|
||||
"rev": "457830c379b8e6763ba147b8c7f83986378b8576",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1528,9 +1547,7 @@
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"taffybar": [
|
||||
"taffybar"
|
||||
],
|
||||
"taffybar": "taffybar",
|
||||
"xmonad": [
|
||||
"xmonad"
|
||||
]
|
||||
@@ -1580,11 +1597,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777261868,
|
||||
"narHash": "sha256-30E1RBr0FGrf1IdXi2OKua+vQ4sUvjwUq6lfC1qcBug=",
|
||||
"lastModified": 1777434099,
|
||||
"narHash": "sha256-GutKXyfGI7o89Dge4bP0yt0CQn1rqA6LyYDOH4GemdE=",
|
||||
"owner": "colonelpanic8",
|
||||
"repo": "keepbook",
|
||||
"rev": "2e178e7ba864af0a880456dca241615dd75afd24",
|
||||
"rev": "240fe454c26e7dbdd25a2fa4f0b436bf1c4f937b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1611,10 +1628,10 @@
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_3",
|
||||
"flake-compat": "flake-compat_4",
|
||||
"flake-parts": "flake-parts",
|
||||
"git-hooks-nix": "git-hooks-nix",
|
||||
"nixpkgs": "nixpkgs_4",
|
||||
"nixpkgs": "nixpkgs_5",
|
||||
"nixpkgs-23-11": "nixpkgs-23-11",
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
@@ -1671,7 +1688,7 @@
|
||||
},
|
||||
"nixos-wsl": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_4",
|
||||
"flake-compat": "flake-compat_5",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
@@ -1805,6 +1822,22 @@
|
||||
}
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1776877367,
|
||||
"narHash": "sha256-EHq1/OX139R1RvBzOJ0aMRT3xnWyqtHBRUBuO1gFzjI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0726a0ecb6d4e08f6adced58726b95db924cef57",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_5": {
|
||||
"locked": {
|
||||
"lastModified": 1771903837,
|
||||
"narHash": "sha256-jEA8WggGKtMFeNeCKq3NK8cLEjJmG6/RLUElYYbBZ0E=",
|
||||
@@ -1817,7 +1850,7 @@
|
||||
"url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz"
|
||||
}
|
||||
},
|
||||
"nixpkgs_5": {
|
||||
"nixpkgs_6": {
|
||||
"locked": {
|
||||
"lastModified": 1776877367,
|
||||
"narHash": "sha256-EHq1/OX139R1RvBzOJ0aMRT3xnWyqtHBRUBuO1gFzjI=",
|
||||
@@ -1833,7 +1866,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_6": {
|
||||
"nixpkgs_7": {
|
||||
"locked": {
|
||||
"lastModified": 1697723726,
|
||||
"narHash": "sha256-SaTWPkI8a5xSHX/rrKzUe+/uVNy6zCGMXgoeMb7T9rg=",
|
||||
@@ -1849,7 +1882,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_7": {
|
||||
"nixpkgs_8": {
|
||||
"locked": {
|
||||
"lastModified": 1703255338,
|
||||
"narHash": "sha256-Z6wfYJQKmDN9xciTwU3cOiOk+NElxdZwy/FiHctCzjU=",
|
||||
@@ -1869,7 +1902,7 @@
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_4",
|
||||
"hercules-ci-effects": "hercules-ci-effects_2",
|
||||
"nixpkgs": "nixpkgs_7",
|
||||
"nixpkgs": "nixpkgs_8",
|
||||
"osx-kvm": "osx-kvm"
|
||||
},
|
||||
"locked": {
|
||||
@@ -2055,9 +2088,10 @@
|
||||
},
|
||||
"pre-commit-hooks_3": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_5",
|
||||
"flake-compat": "flake-compat_3",
|
||||
"gitignore": "gitignore_3",
|
||||
"nixpkgs": [
|
||||
"imalison-taffybar",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
@@ -2148,16 +2182,15 @@
|
||||
"nixified-ai": "nixified-ai",
|
||||
"nixos-hardware": "nixos-hardware",
|
||||
"nixos-wsl": "nixos-wsl",
|
||||
"nixpkgs": "nixpkgs_5",
|
||||
"nixpkgs": "nixpkgs_6",
|
||||
"nixtheplanet": "nixtheplanet",
|
||||
"notifications-tray-icon": "notifications-tray-icon",
|
||||
"org-agenda-api": "org-agenda-api",
|
||||
"railbird-secrets": "railbird-secrets",
|
||||
"systems": "systems_3",
|
||||
"taffybar": "taffybar",
|
||||
"systems": "systems_4",
|
||||
"vscode-server": "vscode-server",
|
||||
"xmonad": "xmonad",
|
||||
"xmonad-contrib": "xmonad-contrib"
|
||||
"xmonad": "xmonad_2",
|
||||
"xmonad-contrib": "xmonad-contrib_2"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
@@ -2259,36 +2292,43 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"taffybar": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"weeder-nix": "weeder-nix",
|
||||
"xmonad": [
|
||||
"xmonad"
|
||||
],
|
||||
"xmonad-contrib": [
|
||||
"xmonad-contrib"
|
||||
]
|
||||
},
|
||||
"systems_4": {
|
||||
"locked": {
|
||||
"lastModified": 1777401169,
|
||||
"narHash": "sha256-bciN/qFjXYm8ZIKXSc/OssUsLt9GoNs/cU9xT/pw7QY=",
|
||||
"owner": "taffybar",
|
||||
"repo": "taffybar",
|
||||
"rev": "59e3c75990156dcd4353ad9fad5823303e751f0f",
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "taffybar",
|
||||
"repo": "taffybar",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"taffybar": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": "nixpkgs_4",
|
||||
"weeder-nix": "weeder-nix",
|
||||
"xmonad": "xmonad",
|
||||
"xmonad-contrib": "xmonad-contrib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777452249,
|
||||
"narHash": "sha256-Emhn9sIFRVyIlUULDuYjeFcYJld6EAD31TGasYwQsWg=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "9a6463e68c7bc0a712e49d9ba6c6d1b764260cd7",
|
||||
"revCount": 2295,
|
||||
"type": "git",
|
||||
"url": "file:///home/imalison/dotfiles/dotfiles/config/taffybar/taffybar"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "file:///home/imalison/dotfiles/dotfiles/config/taffybar/taffybar"
|
||||
}
|
||||
},
|
||||
"vscode-server": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
@@ -2315,6 +2355,7 @@
|
||||
"weeder-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"imalison-taffybar",
|
||||
"taffybar",
|
||||
"nixpkgs"
|
||||
],
|
||||
@@ -2417,20 +2458,7 @@
|
||||
}
|
||||
},
|
||||
"xmonad": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"git-ignore-nix": [
|
||||
"git-ignore-nix"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"unstable": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1776502138,
|
||||
"narHash": "sha256-mSOpNU1iJvfFh5uwayA6aPxneFMduNW1kG1gV2tGE+c=",
|
||||
@@ -2441,11 +2469,29 @@
|
||||
},
|
||||
"original": {
|
||||
"owner": "xmonad",
|
||||
"ref": "master",
|
||||
"repo": "xmonad",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"xmonad-contrib": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1769258911,
|
||||
"narHash": "sha256-YGEKXs4UmS5QOIELJTdCiMzTktuue+Bd3yFoIKSHuBU=",
|
||||
"owner": "xmonad",
|
||||
"repo": "xmonad-contrib",
|
||||
"rev": "803bc3d12bdcc512ec06856c4f119d37de1ba338",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "xmonad",
|
||||
"ref": "master",
|
||||
"repo": "xmonad-contrib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"xmonad-contrib_2": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
@@ -2474,6 +2520,35 @@
|
||||
"repo": "xmonad-contrib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"xmonad_2": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"git-ignore-nix": [
|
||||
"git-ignore-nix"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"unstable": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776502138,
|
||||
"narHash": "sha256-mSOpNU1iJvfFh5uwayA6aPxneFMduNW1kG1gV2tGE+c=",
|
||||
"owner": "xmonad",
|
||||
"repo": "xmonad",
|
||||
"rev": "a618fb32662e44eb5d8276a3dc1925b0233e638b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "xmonad",
|
||||
"repo": "xmonad",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
{ config, pkgs, lib, makeEnable, inputs, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
makeEnable,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.myModules.hyprland;
|
||||
system = pkgs.stdenv.hostPlatform.system;
|
||||
@@ -11,88 +18,7 @@ let
|
||||
inputs.hyprNStack.packages.${system}.hyprNStack
|
||||
inputs.hyprland-plugins-lua.packages.${system}.hyprexpo
|
||||
];
|
||||
hyprexpoPatched = inputs.hyprland-plugins.packages.${system}.hyprexpo.overrideAttrs (old: {
|
||||
patches = (old.patches or [ ]) ++ [
|
||||
./patches/hyprexpo-pr-612-workspace-numbers.patch
|
||||
./patches/hyprexpo-pr-616-bring-mode.patch
|
||||
];
|
||||
});
|
||||
enabledModule = makeEnable config "myModules.hyprland" true {
|
||||
myModules.taffybar.enable = true;
|
||||
|
||||
# Needed for hyprlock authentication without PAM fallback warnings.
|
||||
security.pam.services.hyprlock = {};
|
||||
|
||||
# DDC/CI monitor control for keyboard-driven input switching.
|
||||
hardware.i2c = {
|
||||
enable = true;
|
||||
group = "video";
|
||||
};
|
||||
|
||||
programs.hyprland = {
|
||||
enable = true;
|
||||
# Keep Hyprland and plugins on a matched flake input for ABI compatibility.
|
||||
package = hyprlandInput.packages.${system}.hyprland;
|
||||
# Let UWSM manage the Hyprland session targets
|
||||
withUWSM = true;
|
||||
};
|
||||
|
||||
home-manager.sharedModules = [
|
||||
inputs.hyprscratch.homeModules.default
|
||||
({ config, ... }: {
|
||||
xdg.configFile."hypr" = {
|
||||
force = true;
|
||||
source =
|
||||
if cfg.useLuaConfigBranch
|
||||
then ../dotfiles/config/hypr
|
||||
else config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/dotfiles/dotfiles/config/hypr";
|
||||
};
|
||||
|
||||
services.kanshi = {
|
||||
enable = true;
|
||||
systemdTarget = "graphical-session.target";
|
||||
settings = [
|
||||
{
|
||||
# USB-C connector names can move between DP-* ports across docks/reboots.
|
||||
# Match the ultrawide by make/model and allow the serial field to vary.
|
||||
profile.name = "ultrawide-usbc-desk";
|
||||
profile.outputs = [
|
||||
{
|
||||
criteria = "eDP-1";
|
||||
status = "enable";
|
||||
mode = "2560x1600@240Hz";
|
||||
position = "0,0";
|
||||
scale = 1.0;
|
||||
}
|
||||
{
|
||||
criteria = "Microstep MPG341CX OLED *";
|
||||
status = "enable";
|
||||
mode = "3440x1440@240Hz";
|
||||
position = "2560,0";
|
||||
scale = 1.0;
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
# When the laptop panel is unavailable (e.g. lid-closed docked use),
|
||||
# still drive the ultrawide at its full refresh rate.
|
||||
profile.name = "ultrawide-only";
|
||||
profile.outputs = [
|
||||
{
|
||||
criteria = "Microstep MPG341CX OLED *";
|
||||
status = "enable";
|
||||
mode = "3440x1440@240Hz";
|
||||
position = "0,0";
|
||||
scale = 1.0;
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
programs.hyprscratch = {
|
||||
enable = !cfg.useLuaConfigBranch;
|
||||
settings = {
|
||||
hyprscratchSettings = {
|
||||
daemon_options = "clean";
|
||||
global_options = "";
|
||||
global_rules = "float;size monitor_w*0.95 monitor_h*0.95;center";
|
||||
@@ -146,12 +72,112 @@ let
|
||||
title = "Messages";
|
||||
};
|
||||
};
|
||||
enabledModule = makeEnable config "myModules.hyprland" true {
|
||||
myModules.taffybar.enable = true;
|
||||
|
||||
# Needed for hyprlock authentication without PAM fallback warnings.
|
||||
security.pam.services.hyprlock = { };
|
||||
|
||||
# DDC/CI monitor control for keyboard-driven input switching.
|
||||
hardware.i2c = {
|
||||
enable = true;
|
||||
group = "video";
|
||||
};
|
||||
})
|
||||
|
||||
programs.hyprland = {
|
||||
enable = true;
|
||||
# Keep Hyprland and plugins on a matched flake input for ABI compatibility.
|
||||
package = hyprlandInput.packages.${system}.hyprland;
|
||||
# Let UWSM manage the Hyprland session targets
|
||||
withUWSM = true;
|
||||
};
|
||||
|
||||
home-manager.sharedModules = [
|
||||
inputs.hyprscratch.homeModules.default
|
||||
(
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
hyprConfig =
|
||||
name:
|
||||
config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/dotfiles/dotfiles/config/hypr/${name}";
|
||||
in
|
||||
{
|
||||
services.kanshi = {
|
||||
enable = true;
|
||||
systemdTarget = "graphical-session.target";
|
||||
settings = [
|
||||
{
|
||||
# USB-C connector names can move between DP-* ports across docks/reboots.
|
||||
# Match the ultrawide by make/model and allow the serial field to vary.
|
||||
profile.name = "ultrawide-usbc-desk";
|
||||
profile.outputs = [
|
||||
{
|
||||
criteria = "eDP-1";
|
||||
status = "enable";
|
||||
mode = "2560x1600@240Hz";
|
||||
position = "0,0";
|
||||
scale = 1.0;
|
||||
}
|
||||
{
|
||||
criteria = "Microstep MPG341CX OLED *";
|
||||
status = "enable";
|
||||
mode = "3440x1440@240Hz";
|
||||
position = "2560,0";
|
||||
scale = 1.0;
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
# When the laptop panel is unavailable (e.g. lid-closed docked use),
|
||||
# still drive the ultrawide at its full refresh rate.
|
||||
profile.name = "ultrawide-only";
|
||||
profile.outputs = [
|
||||
{
|
||||
criteria = "Microstep MPG341CX OLED *";
|
||||
status = "enable";
|
||||
mode = "3440x1440@240Hz";
|
||||
position = "0,0";
|
||||
scale = 1.0;
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
programs.hyprscratch = {
|
||||
enable = !cfg.useLuaConfigBranch;
|
||||
settings = { };
|
||||
};
|
||||
|
||||
xdg.configFile."hyprscratch/config.conf" = lib.mkIf (!cfg.useLuaConfigBranch) {
|
||||
text = lib.hm.generators.toHyprconf {
|
||||
attrs = hyprscratchSettings;
|
||||
};
|
||||
};
|
||||
|
||||
xdg.configFile."hypr/hyprland.conf" = {
|
||||
force = true;
|
||||
source = hyprConfig "hyprland.conf";
|
||||
};
|
||||
|
||||
xdg.configFile."hypr/hyprland.lua" = lib.mkIf cfg.useLuaConfigBranch {
|
||||
force = true;
|
||||
source = hyprConfig "hyprland.lua";
|
||||
};
|
||||
|
||||
xdg.configFile."hypr/hypridle.conf".source = hyprConfig "hypridle.conf";
|
||||
|
||||
xdg.configFile."hypr/hyprlock.conf".source = hyprConfig "hyprlock.conf";
|
||||
|
||||
xdg.configFile."hypr/scripts".enable = false;
|
||||
}
|
||||
)
|
||||
];
|
||||
|
||||
# Hyprland-specific packages
|
||||
environment.systemPackages = with pkgs; [
|
||||
environment.systemPackages =
|
||||
with pkgs;
|
||||
[
|
||||
# Hyprland utilities
|
||||
hyprpaper # Wallpaper
|
||||
hypridle # Idle daemon
|
||||
@@ -164,21 +190,24 @@ let
|
||||
slurp # Region selection
|
||||
swappy # Screenshot annotation
|
||||
nwg-displays # GUI monitor arrangement
|
||||
mpv # Graphical screensaver payload
|
||||
mpvpaper # Layer-shell video screensaver payload
|
||||
ddcutil # Monitor input switching over DDC/CI
|
||||
|
||||
# For scripts
|
||||
jq
|
||||
] ++ luaPluginPackages ++ lib.optionals enableExternalPluginPackages [
|
||||
]
|
||||
++ luaPluginPackages
|
||||
++ lib.optionals enableExternalPluginPackages [
|
||||
# External plugin packages are pinned to the stable 0.53 stack.
|
||||
# Keep hy3 on the stable stack; the Lua branch uses hyprNStack and the
|
||||
# forked Lua-compatible hyprexpo input instead.
|
||||
inputs.hy3.packages.${system}.hy3
|
||||
hyprexpoPatched
|
||||
inputs.hyprland-plugins.packages.${system}.hyprexpo
|
||||
];
|
||||
};
|
||||
in
|
||||
enabledModule // {
|
||||
enabledModule
|
||||
// {
|
||||
options = lib.recursiveUpdate enabledModule.options {
|
||||
myModules.hyprland.useLuaConfigBranch = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
From aaefc0ff0bc4348de04f311ad0101da44c62ae94 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Malison <IvanMalison@gmail.com>
|
||||
Date: Wed, 4 Feb 2026 00:54:52 -0800
|
||||
Subject: [PATCH 1/2] hyprexpo: optionally render workspace numbers
|
||||
|
||||
---
|
||||
hyprexpo/README.md | 3 +-
|
||||
hyprexpo/main.cpp | 2 +
|
||||
hyprexpo/overview.cpp | 109 ++++++++++++++++++++++++++++++++++++++++++
|
||||
hyprexpo/overview.hpp | 4 ++
|
||||
4 files changed, 117 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/README.md b/README.md
|
||||
index 97bd1d4..aac2e97 100644
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
@@ -28,6 +28,8 @@ gap_size | number | gap between desktops | `5`
|
||||
bg_col | color | color in gaps (between desktops) | `rgb(000000)`
|
||||
workspace_method | [center/first] [workspace] | position of the desktops | `center current`
|
||||
skip_empty | boolean | whether the grid displays workspaces sequentially by id using selector "r" (`false`) or skips empty workspaces using selector "m" (`true`) | `false`
|
||||
+show_workspace_numbers | boolean | show numeric labels for workspaces | `false`
|
||||
+workspace_number_color | color | color of workspace number labels | `rgb(ffffff)`
|
||||
gesture_distance | number | how far is the max for the gesture | `300`
|
||||
|
||||
### Keywords
|
||||
@@ -57,4 +59,3 @@ off | hides the overview
|
||||
disable | same as `off`
|
||||
on | displays the overview
|
||||
enable | same as `on`
|
||||
-
|
||||
diff --git a/main.cpp b/main.cpp
|
||||
index 883fd82..ff9f380 100644
|
||||
--- a/main.cpp
|
||||
+++ b/main.cpp
|
||||
@@ -239,6 +239,8 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:bg_col", Hyprlang::INT{0xFF111111});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:workspace_method", Hyprlang::STRING{"center current"});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:skip_empty", Hyprlang::INT{0});
|
||||
+ HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:show_workspace_numbers", Hyprlang::INT{0});
|
||||
+ HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:workspace_number_color", Hyprlang::INT{0xFFFFFFFF});
|
||||
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:gesture_distance", Hyprlang::INT{200});
|
||||
|
||||
diff --git a/overview.cpp b/overview.cpp
|
||||
index 5721948..926a9f8 100644
|
||||
--- a/overview.cpp
|
||||
+++ b/overview.cpp
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "overview.hpp"
|
||||
#include <any>
|
||||
+#include <algorithm>
|
||||
+#include <cmath>
|
||||
+#include <pango/pangocairo.h>
|
||||
#define private public
|
||||
#include <hyprland/src/render/Renderer.hpp>
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
@@ -15,6 +18,86 @@
|
||||
#undef private
|
||||
#include "OverviewPassElement.hpp"
|
||||
|
||||
+static Vector2D renderLabelTexture(SP<CTexture> out, const std::string& text, const CHyprColor& color, int fontSizePx) {
|
||||
+ if (!out || text.empty() || fontSizePx <= 0)
|
||||
+ return {};
|
||||
+
|
||||
+ auto measureSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
|
||||
+ auto measureCairo = cairo_create(measureSurface);
|
||||
+
|
||||
+ PangoLayout* measureLayout = pango_cairo_create_layout(measureCairo);
|
||||
+ pango_layout_set_text(measureLayout, text.c_str(), -1);
|
||||
+ auto* fontDesc = pango_font_description_from_string("Sans Bold");
|
||||
+ pango_font_description_set_size(fontDesc, fontSizePx * PANGO_SCALE);
|
||||
+ pango_layout_set_font_description(measureLayout, fontDesc);
|
||||
+ pango_font_description_free(fontDesc);
|
||||
+
|
||||
+ PangoRectangle inkRect, logicalRect;
|
||||
+ pango_layout_get_extents(measureLayout, &inkRect, &logicalRect);
|
||||
+
|
||||
+ const int textW = std::max(1, (int)std::ceil(logicalRect.width / (double)PANGO_SCALE));
|
||||
+ const int textH = std::max(1, (int)std::ceil(logicalRect.height / (double)PANGO_SCALE));
|
||||
+
|
||||
+ g_object_unref(measureLayout);
|
||||
+ cairo_destroy(measureCairo);
|
||||
+ cairo_surface_destroy(measureSurface);
|
||||
+
|
||||
+ const int pad = std::max(4, (int)std::round(fontSizePx * 0.35));
|
||||
+ const int width = textW + pad * 2;
|
||||
+ const int height = textH + pad * 2;
|
||||
+
|
||||
+ auto surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
|
||||
+ auto cairo = cairo_create(surface);
|
||||
+
|
||||
+ // Clear the pixmap
|
||||
+ cairo_save(cairo);
|
||||
+ cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
|
||||
+ cairo_paint(cairo);
|
||||
+ cairo_restore(cairo);
|
||||
+
|
||||
+ // Background for legibility
|
||||
+ cairo_set_source_rgba(cairo, 0.0, 0.0, 0.0, 0.55);
|
||||
+ cairo_rectangle(cairo, 0, 0, width, height);
|
||||
+ cairo_fill(cairo);
|
||||
+
|
||||
+ PangoLayout* layout = pango_cairo_create_layout(cairo);
|
||||
+ pango_layout_set_text(layout, text.c_str(), -1);
|
||||
+ fontDesc = pango_font_description_from_string("Sans Bold");
|
||||
+ pango_font_description_set_size(fontDesc, fontSizePx * PANGO_SCALE);
|
||||
+ pango_layout_set_font_description(layout, fontDesc);
|
||||
+ pango_font_description_free(fontDesc);
|
||||
+
|
||||
+ pango_layout_get_extents(layout, &inkRect, &logicalRect);
|
||||
+ const double xOffset = (width - logicalRect.width / (double)PANGO_SCALE) / 2.0;
|
||||
+ const double yOffset = (height - logicalRect.height / (double)PANGO_SCALE) / 2.0;
|
||||
+
|
||||
+ cairo_set_source_rgba(cairo, color.r, color.g, color.b, color.a);
|
||||
+ cairo_move_to(cairo, xOffset, yOffset);
|
||||
+ pango_cairo_show_layout(cairo, layout);
|
||||
+
|
||||
+ g_object_unref(layout);
|
||||
+
|
||||
+ cairo_surface_flush(surface);
|
||||
+
|
||||
+ const auto DATA = cairo_image_surface_get_data(surface);
|
||||
+ out->allocate();
|
||||
+ glBindTexture(GL_TEXTURE_2D, out->m_texID);
|
||||
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
+
|
||||
+#ifndef GLES2
|
||||
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
+#endif
|
||||
+
|
||||
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||
+
|
||||
+ cairo_destroy(cairo);
|
||||
+ cairo_surface_destroy(surface);
|
||||
+
|
||||
+ return {width, height};
|
||||
+}
|
||||
+
|
||||
static void damageMonitor(WP<Hyprutils::Animation::CBaseAnimatedVariable> thisptr) {
|
||||
g_pOverview->damage();
|
||||
}
|
||||
@@ -34,11 +117,14 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn
|
||||
static auto* const* PGAPS = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:gap_size")->getDataStaticPtr();
|
||||
static auto* const* PCOL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:bg_col")->getDataStaticPtr();
|
||||
static auto* const* PSKIP = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:skip_empty")->getDataStaticPtr();
|
||||
+ static auto* const* PSHOWNUM = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:show_workspace_numbers")->getDataStaticPtr();
|
||||
+ static auto* const* PNUMCOL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:workspace_number_color")->getDataStaticPtr();
|
||||
static auto const* PMETHOD = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:workspace_method")->getDataStaticPtr();
|
||||
|
||||
SIDE_LENGTH = **PCOLUMNS;
|
||||
GAP_WIDTH = **PGAPS;
|
||||
BG_COLOR = **PCOL;
|
||||
+ showWorkspaceNumbers = **PSHOWNUM;
|
||||
|
||||
// process the method
|
||||
bool methodCenter = true;
|
||||
@@ -126,6 +212,17 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn
|
||||
Vector2D tileRenderSize = (pMonitor->m_size - Vector2D{GAP_WIDTH * pMonitor->m_scale, GAP_WIDTH * pMonitor->m_scale} * (SIDE_LENGTH - 1)) / SIDE_LENGTH;
|
||||
CBox monbox{0, 0, tileSize.x * 2, tileSize.y * 2};
|
||||
|
||||
+ if (showWorkspaceNumbers) {
|
||||
+ const CHyprColor numberColor = **PNUMCOL;
|
||||
+ const int fontSizePx = std::max(12, (int)std::round(tileRenderSize.y * pMonitor->m_scale * 0.22));
|
||||
+ for (auto& image : images) {
|
||||
+ if (image.workspaceID == WORKSPACE_INVALID)
|
||||
+ continue;
|
||||
+ image.labelTex = makeShared<CTexture>();
|
||||
+ image.labelSizePx = renderLabelTexture(image.labelTex, std::to_string(image.workspaceID), numberColor, fontSizePx);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
if (!ENABLE_LOWRES)
|
||||
monbox = {{0, 0}, pMonitor->m_pixelSize};
|
||||
|
||||
@@ -452,6 +549,18 @@ void COverview::fullRender() {
|
||||
texbox.round();
|
||||
CRegion damage{0, 0, INT16_MAX, INT16_MAX};
|
||||
g_pHyprOpenGL->renderTextureInternal(images[x + y * SIDE_LENGTH].fb.getTexture(), texbox, {.damage = &damage, .a = 1.0});
|
||||
+
|
||||
+ if (showWorkspaceNumbers) {
|
||||
+ auto& image = images[x + y * SIDE_LENGTH];
|
||||
+ if (image.workspaceID != WORKSPACE_INVALID && image.labelTex && image.labelTex->m_texID != 0 && image.labelSizePx.x > 0 && image.labelSizePx.y > 0) {
|
||||
+ const Vector2D labelSize = image.labelSizePx / pMonitor->m_scale;
|
||||
+ const float margin = std::max(4.0, tileRenderSize.y * 0.05);
|
||||
+ CBox labelBox = {x * tileRenderSize.x + x * GAPSIZE + margin, y * tileRenderSize.y + y * GAPSIZE + margin, labelSize.x, labelSize.y};
|
||||
+ labelBox.scale(pMonitor->m_scale).translate(pos->value());
|
||||
+ labelBox.round();
|
||||
+ g_pHyprOpenGL->renderTexture(image.labelTex, labelBox, {.a = 1.0});
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/overview.hpp b/overview.hpp
|
||||
index 4b02400..1f6bf3c 100644
|
||||
--- a/overview.hpp
|
||||
+++ b/overview.hpp
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <hyprland/src/helpers/AnimatedVariable.hpp>
|
||||
#include <hyprland/src/managers/HookSystemManager.hpp>
|
||||
#include <vector>
|
||||
+class CTexture;
|
||||
|
||||
// saves on resources, but is a bit broken rn with blur.
|
||||
// hyprland's fault, but cba to fix.
|
||||
@@ -58,6 +59,8 @@ class COverview {
|
||||
int64_t workspaceID = -1;
|
||||
PHLWORKSPACE pWorkspace;
|
||||
CBox box;
|
||||
+ SP<CTexture> labelTex;
|
||||
+ Vector2D labelSizePx;
|
||||
};
|
||||
|
||||
Vector2D lastMousePosLocal = Vector2D{};
|
||||
@@ -81,6 +84,7 @@ class COverview {
|
||||
|
||||
bool swipe = false;
|
||||
bool swipeWasCommenced = false;
|
||||
+ bool showWorkspaceNumbers = false;
|
||||
|
||||
friend class COverviewPassElement;
|
||||
};
|
||||
--
|
||||
2.52.0
|
||||
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
From edc05ce88f79ceda0cdcb9aa68ec371b1af323de Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Malison <IvanMalison@gmail.com>
|
||||
Date: Mon, 16 Feb 2026 21:50:16 -0800
|
||||
Subject: [PATCH 2/2] hyprexpo: add bring selection mode
|
||||
|
||||
---
|
||||
hyprexpo/README.md | 1 +
|
||||
hyprexpo/main.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++
|
||||
hyprexpo/overview.cpp | 12 +++++++++--
|
||||
hyprexpo/overview.hpp | 3 ++-
|
||||
4 files changed, 63 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/README.md b/README.md
|
||||
index aac2e97..084f02b 100644
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
@@ -55,6 +55,7 @@ Here are a list of options you can use:
|
||||
| --- | --- |
|
||||
toggle | displays if hidden, hide if displayed
|
||||
select | selects the hovered desktop
|
||||
+bring | brings a window from the hovered desktop to the current desktop
|
||||
off | hides the overview
|
||||
disable | same as `off`
|
||||
on | displays the overview
|
||||
diff --git a/main.cpp b/main.cpp
|
||||
index ff9f380..78bac24 100644
|
||||
--- a/main.cpp
|
||||
+++ b/main.cpp
|
||||
@@ -65,6 +65,47 @@ static void hkAddDamageB(void* thisptr, const pixman_region32_t* rg) {
|
||||
g_pOverview->onDamageReported();
|
||||
}
|
||||
|
||||
+static PHLWINDOW windowToBringFromWorkspace(const PHLWORKSPACE& workspace) {
|
||||
+ if (!workspace)
|
||||
+ return nullptr;
|
||||
+
|
||||
+ for (auto it = g_pCompositor->m_windows.rbegin(); it != g_pCompositor->m_windows.rend(); ++it) {
|
||||
+ const auto& w = *it;
|
||||
+ if (!w || w->m_workspace != workspace || !w->m_isMapped || w->isHidden())
|
||||
+ continue;
|
||||
+
|
||||
+ return w;
|
||||
+ }
|
||||
+
|
||||
+ return nullptr;
|
||||
+}
|
||||
+
|
||||
+static SDispatchResult bringWindowFromWorkspace(int64_t sourceWorkspaceID) {
|
||||
+ if (sourceWorkspaceID == WORKSPACE_INVALID)
|
||||
+ return {.success = false, .error = "selected workspace is empty"};
|
||||
+
|
||||
+ const auto FOCUSSTATE = Desktop::focusState();
|
||||
+ const auto MONITOR = FOCUSSTATE->monitor();
|
||||
+ if (!MONITOR || !MONITOR->m_activeWorkspace)
|
||||
+ return {.success = false, .error = "no active monitor/workspace"};
|
||||
+
|
||||
+ if (sourceWorkspaceID == MONITOR->activeWorkspaceID())
|
||||
+ return {};
|
||||
+
|
||||
+ const auto SOURCEWORKSPACE = g_pCompositor->getWorkspaceByID(sourceWorkspaceID);
|
||||
+ if (!SOURCEWORKSPACE)
|
||||
+ return {.success = false, .error = "selected workspace is not open"};
|
||||
+
|
||||
+ const auto WINDOW = windowToBringFromWorkspace(SOURCEWORKSPACE);
|
||||
+ if (!WINDOW)
|
||||
+ return {.success = false, .error = "selected workspace has no mapped windows"};
|
||||
+
|
||||
+ g_pCompositor->moveWindowToWorkspaceSafe(WINDOW, MONITOR->m_activeWorkspace);
|
||||
+ FOCUSSTATE->fullWindowFocus(WINDOW);
|
||||
+ g_pCompositor->warpCursorTo(WINDOW->middle());
|
||||
+ return {};
|
||||
+}
|
||||
+
|
||||
static SDispatchResult onExpoDispatcher(std::string arg) {
|
||||
|
||||
if (g_pOverview && g_pOverview->m_isSwiping)
|
||||
@@ -77,6 +118,15 @@ static SDispatchResult onExpoDispatcher(std::string arg) {
|
||||
}
|
||||
return {};
|
||||
}
|
||||
+ if (arg == "bring") {
|
||||
+ if (g_pOverview) {
|
||||
+ g_pOverview->selectHoveredWorkspace();
|
||||
+ const auto BRINGRESULT = bringWindowFromWorkspace(g_pOverview->selectedWorkspaceID());
|
||||
+ g_pOverview->close(false);
|
||||
+ return BRINGRESULT;
|
||||
+ }
|
||||
+ return {};
|
||||
+ }
|
||||
if (arg == "toggle") {
|
||||
if (g_pOverview)
|
||||
g_pOverview->close();
|
||||
diff --git a/overview.cpp b/overview.cpp
|
||||
index 926a9f8..45ee982 100644
|
||||
--- a/overview.cpp
|
||||
+++ b/overview.cpp
|
||||
@@ -343,6 +343,14 @@ void COverview::selectHoveredWorkspace() {
|
||||
closeOnID = x + y * SIDE_LENGTH;
|
||||
}
|
||||
|
||||
+int64_t COverview::selectedWorkspaceID() const {
|
||||
+ const int ID = closeOnID == -1 ? openedID : closeOnID;
|
||||
+ if (ID < 0 || ID >= (int)images.size())
|
||||
+ return WORKSPACE_INVALID;
|
||||
+
|
||||
+ return images[ID].workspaceID;
|
||||
+}
|
||||
+
|
||||
void COverview::redrawID(int id, bool forcelowres) {
|
||||
if (!pMonitor)
|
||||
return;
|
||||
@@ -451,7 +459,7 @@ void COverview::onDamageReported() {
|
||||
g_pCompositor->scheduleFrameForMonitor(pMonitor.lock());
|
||||
}
|
||||
|
||||
-void COverview::close() {
|
||||
+void COverview::close(bool switchToSelection) {
|
||||
if (closing)
|
||||
return;
|
||||
|
||||
@@ -471,7 +479,7 @@ void COverview::close() {
|
||||
|
||||
redrawAll();
|
||||
|
||||
- if (TILE.workspaceID != pMonitor->activeWorkspaceID()) {
|
||||
+ if (switchToSelection && TILE.workspaceID != pMonitor->activeWorkspaceID()) {
|
||||
pMonitor->setSpecialWorkspace(0);
|
||||
|
||||
// If this tile's workspace was WORKSPACE_INVALID, move to the next
|
||||
diff --git a/overview.hpp b/overview.hpp
|
||||
index 1f6bf3c..ca59f32 100644
|
||||
--- a/overview.hpp
|
||||
+++ b/overview.hpp
|
||||
@@ -33,8 +33,9 @@ class COverview {
|
||||
void onSwipeEnd();
|
||||
|
||||
// close without a selection
|
||||
- void close();
|
||||
+ void close(bool switchToSelection = true);
|
||||
void selectHoveredWorkspace();
|
||||
+ int64_t selectedWorkspaceID() const;
|
||||
|
||||
bool blockOverviewRendering = false;
|
||||
bool blockDamageReporting = false;
|
||||
--
|
||||
2.52.0
|
||||
|
||||
Reference in New Issue
Block a user