diff --git a/dots/bin/hypr-snap.sh b/dots/bin/hypr-snap.sh index 6e306f4..21fc7ee 100644 --- a/dots/bin/hypr-snap.sh +++ b/dots/bin/hypr-snap.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # hypr-snap.sh -# Move a window in a direction. If already at the edge, snap to fill that edge. -# Works dynamically across any monitor configuration. +# Move a window in a direction. If already touching that edge, snap to fill +# the full usable area of the monitor. Works across any monitor configuration. # # Usage: hypr-snap.sh # @@ -18,71 +18,86 @@ if [[ -z "$DIRECTION" ]]; then exit 1 fi -# --- get active window info --- WIN=$(hyprctl activewindow -j) -WIN_X=$(echo "$WIN" | jq '.at[0]') -WIN_Y=$(echo "$WIN" | jq '.at[1]') -WIN_W=$(echo "$WIN" | jq '.size[0]') -WIN_H=$(echo "$WIN" | jq '.size[1]') -WIN_R=$((WIN_X + WIN_W)) -WIN_B=$((WIN_Y + WIN_H)) - -# --- get the focused monitor info --- MON=$(hyprctl monitors -j | jq '.[] | select(.focused == true)') -MON_X=$(echo "$MON" | jq '.x') -MON_Y=$(echo "$MON" | jq '.y') -MON_SCALE=$(echo "$MON" | jq '.scale') -# reserved areas (e.g. status bar): [top, bottom, left, right] -RES_T=$(echo "$MON" | jq '.reserved[0]') -RES_B=$(echo "$MON" | jq '.reserved[1]') -RES_L=$(echo "$MON" | jq '.reserved[2]') -RES_R=$(echo "$MON" | jq '.reserved[3]') +# Do all math inside jq to avoid shell integer truncation and floating point issues. +# A window is considered "at the edge" when it is within TOLERANCE pixels of it. +DATA=$(jq -n \ + --argjson win "$WIN" \ + --argjson mon "$MON" ' + ($mon.width / $mon.scale | floor) as $mw | + ($mon.height / $mon.scale | floor) as $mh | + ($mon.reserved[2]) as $res_l | + ($mon.reserved[3]) as $res_r | + ($mon.reserved[0]) as $res_t | + ($mon.reserved[1]) as $res_b | + { + win_x: $win.at[0], + win_y: $win.at[1], + win_r: ($win.at[0] + $win.size[0]), + win_b: ($win.at[1] + $win.size[1]), + floating: $win.floating, -# usable area on this monitor, accounting for scale and reserved regions -USE_X=$(echo "$MON" | jq "($MON_X + $RES_L) | floor") -USE_Y=$(echo "$MON" | jq "($MON_Y + $RES_T) | floor") -USE_W=$(echo "$MON" | jq "((.width / $MON_SCALE) - $RES_L - $RES_R) | floor") -USE_H=$(echo "$MON" | jq "((.height / $MON_SCALE) - $RES_T - $RES_B) | floor") -USE_R=$((USE_X + USE_W)) -USE_B=$((USE_Y + USE_H)) + use_x: ($mon.x + $res_l), + use_y: ($mon.y + $res_t), + use_w: ($mw - $res_l - $res_r), + use_h: ($mh - $res_t - $res_b), + use_r: ($mon.x + $mw - $res_r), + use_b: ($mon.y + $mh - $res_b) + } +') + +WIN_X=$(echo "$DATA" | jq '.win_x') +WIN_Y=$(echo "$DATA" | jq '.win_y') +WIN_R=$(echo "$DATA" | jq '.win_r') +WIN_B=$(echo "$DATA" | jq '.win_b') +FLOATING=$(echo "$DATA" | jq '.floating') + +USE_X=$(echo "$DATA" | jq '.use_x') +USE_Y=$(echo "$DATA" | jq '.use_y') +USE_W=$(echo "$DATA" | jq '.use_w') +USE_H=$(echo "$DATA" | jq '.use_h') +USE_R=$(echo "$DATA" | jq '.use_r') +USE_B=$(echo "$DATA" | jq '.use_b') + +# How many pixels past the edge the window needs to reach before snapping. +# movewindoworgroup moves in steps, so set this to at least one step size. +TOLERANCE=50 snap() { - local x=$1 y=$2 w=$3 h=$4 - # if window is tiled, make it floating first so we can freely position/resize it - FLOATING=$(echo "$WIN" | jq '.floating') if [[ "$FLOATING" != "true" ]]; then hyprctl dispatch togglefloating fi - hyprctl dispatch moveactive exact "$x" "$y" - hyprctl dispatch resizeactive exact "$w" "$h" + hyprctl dispatch moveactive exact "$USE_X" "$USE_Y" + hyprctl dispatch resizeactive exact "$USE_W" "$USE_H" } case $DIRECTION in l) - if [[ "$WIN_X" -le "$USE_X" ]]; then - snap "$USE_X" "$USE_Y" "$USE_W" "$USE_H" + if [[ "$WIN_X" -le $((USE_X + TOLERANCE)) ]]; then + snap else hyprctl dispatch movewindoworgroup l fi ;; r) - if [[ "$WIN_R" -ge "$USE_R" ]]; then - snap "$USE_X" "$USE_Y" "$USE_W" "$USE_H" + if [[ "$WIN_R" -ge $((USE_R - TOLERANCE)) ]]; then + snap else hyprctl dispatch movewindoworgroup r fi ;; u) - if [[ "$WIN_Y" -le "$USE_Y" ]]; then - snap "$USE_X" "$USE_Y" "$USE_W" "$USE_H" + if [[ "$WIN_Y" -le $((USE_Y + TOLERANCE)) ]]; then + snap else hyprctl dispatch movewindoworgroup u fi ;; d) - if [[ "$WIN_B" -ge "$USE_B" ]]; then - snap "$USE_X" "$USE_Y" "$USE_W" "$USE_H" + if [[ "$WIN_B" -ge $((USE_B - TOLERANCE)) ]]; then + snap else hyprctl dispatch movewindoworgroup d fi