diff --git a/dot_local/bin/executable_omarchy-pip b/dot_local/bin/executable_omarchy-pip new file mode 100755 index 0000000..00b7ca1 --- /dev/null +++ b/dot_local/bin/executable_omarchy-pip @@ -0,0 +1,70 @@ +#!/bin/bash + +# omarchy:summary=Toggle a window between picture-in-picture (floating, pinned, bottom-left corner of the focused monitor) and tiled. +# omarchy:args=[toggle|on] [address:0x...] + +set -uo pipefail + +# Usage: omarchy-pip [toggle|on] [address:0x...] +# +# "on" always fits the window: float, pin, resize to FRACTION of the focused +# monitor on both axes and park it in the bottom-left corner, inset by GAP. +# "toggle" (the default) does that unless the window is already floating and +# pinned, in which case it unpins and tiles it again. The window defaults to +# the active one. omarchy-summon calls "on" after carrying its window to the +# focused monitor; the YouTube window rule in hypr/hyprland.lua mirrors the +# same fraction and inset for the first open, so keep the three in sync. +MODE=${1:-toggle} +WINDOW=${2:-} +case $MODE in +toggle | on) ;; +*) + echo "usage: ${0##*/} [toggle|on] [address:0x...]" >&2 + exit 2 + ;; +esac + +FRACTION=0.3 +# Matches general:gaps_out so the window lines up with tiled neighbours and +# its border stays on screen. +GAP=10 + +# Lua dispatcher first, classic name as the pre-lua fallback (same shape as +# the other omarchy-* scripts here). +hypr_dispatch() { + local lua="$1" + shift + hyprctl dispatch "$lua" >/dev/null 2>&1 || hyprctl dispatch "$@" >/dev/null +} + +if [[ -z $WINDOW ]]; then + ACTIVE=$(hyprctl activewindow -j 2>/dev/null) + [[ -n $ACTIVE && $ACTIVE != "{}" ]] || exit 0 + WINDOW="address:$(jq -r '.address' <<<"$ACTIVE")" +else + ACTIVE=$(hyprctl clients -j | jq -c --arg a "${WINDOW#address:}" 'first(.[] | select(.address == $a)) // empty') + [[ -n $ACTIVE ]] || exit 1 +fi + +# The wiki documents action = "set", but on Hyprland 0.56 "set" toggles; +# "on" / "off" are what the dispatcher actually honours. +if [[ $MODE == toggle ]] && [[ $(jq -r '.floating and .pinned' <<<"$ACTIVE") == true ]]; then + hypr_dispatch "hl.dsp.window.pin({ action = \"off\", window = \"$WINDOW\" })" pin "$WINDOW" + hypr_dispatch "hl.dsp.window.float({ action = \"off\", window = \"$WINDOW\" })" settiled "$WINDOW" + exit 0 +fi + +# Focused monitor: origin and logical size. +read -r MON_X MON_Y MON_W MON_H < <(hyprctl monitors -j | + jq -r '.[] | select(.focused) | "\(.x) \(.y) \(((.width / .scale) | floor)) \(((.height / .scale) | floor))"') +[[ -n ${MON_H:-} ]] || exit 1 + +WIDTH=$(awk "BEGIN { printf \"%d\", $MON_W * $FRACTION }") +HEIGHT=$(awk "BEGIN { printf \"%d\", $MON_H * $FRACTION }") +X=$((MON_X + GAP)) +Y=$((MON_Y + MON_H - HEIGHT - GAP)) + +hypr_dispatch "hl.dsp.window.float({ action = \"on\", window = \"$WINDOW\" })" setfloating "$WINDOW" +hypr_dispatch "hl.dsp.window.pin({ action = \"on\", window = \"$WINDOW\" })" pin "$WINDOW" +hypr_dispatch "hl.dsp.window.resize({ x = $WIDTH, y = $HEIGHT, window = \"$WINDOW\" })" resizewindowpixel "exact $WIDTH $HEIGHT,$WINDOW" +hypr_dispatch "hl.dsp.window.move({ x = $X, y = $Y, window = \"$WINDOW\" })" movewindowpixel "exact $X $Y,$WINDOW" diff --git a/dot_local/bin/executable_omarchy-smart-move b/dot_local/bin/executable_omarchy-smart-move index 54a14cd..32031b8 100755 --- a/dot_local/bin/executable_omarchy-smart-move +++ b/dot_local/bin/executable_omarchy-smart-move @@ -1,6 +1,6 @@ #!/bin/bash -# omarchy:summary=Move the focused window into a neighboring group, or swap with the neighbor +# omarchy:summary=Move the focused window into a neighboring group or swap with the neighbor; floating windows go to a screen corner # omarchy:args= set -uo pipefail @@ -31,6 +31,33 @@ GROUPED=$(jq -r '(.grouped // []) | length' <<<"$ACTIVE") [[ -n $ADDRESS && -n $WORKSPACE ]] || exit 0 +# A floating window has no neighbours to swap with, so the four keys park it +# in a corner of its monitor instead, size unchanged, inside the reserved +# area (the bar) and inset by GAP (matches general:gaps_out, same as +# omarchy-pip). h/j/k/l read clockwise from the bottom left: l bottom-left, +# d top-left, u top-right, r bottom-right. +if [[ $(jq -r '.floating' <<<"$ACTIVE") == true ]]; then + GAP=10 + read -r WIN_W WIN_H MON_ID < <(jq -r '"\(.size[0]) \(.size[1]) \(.monitor)"' <<<"$ACTIVE") + read -r MON_X MON_Y MON_W MON_H RES_L RES_T RES_R RES_B < <(hyprctl monitors -j | jq -r --argjson id "$MON_ID" \ + '.[] | select(.id == $id) | "\(.x) \(.y) \(((.width / .scale) | floor)) \(((.height / .scale) | floor)) \(.reserved | join(" "))"') + [[ -n ${RES_B:-} ]] || exit 1 + + LEFT=$((MON_X + RES_L + GAP)) + RIGHT=$((MON_X + MON_W - RES_R - WIN_W - GAP)) + TOP=$((MON_Y + RES_T + GAP)) + BOTTOM=$((MON_Y + MON_H - RES_B - WIN_H - GAP)) + case $DIR in + l) X=$LEFT Y=$BOTTOM ;; + d) X=$LEFT Y=$TOP ;; + u) X=$RIGHT Y=$TOP ;; + r) X=$RIGHT Y=$BOTTOM ;; + esac + + hypr_dispatch "hl.dsp.window.move({ x = $X, y = $Y })" movewindowpixel "exact $X $Y,address:$ADDRESS" + exit 0 +fi + # Find the nearest window in the given direction. A window counts as a # neighbor only when it overlaps the focused window on the other axis, so a # window in a far corner is not picked. diff --git a/dot_local/bin/executable_omarchy-summon b/dot_local/bin/executable_omarchy-summon new file mode 100755 index 0000000..7a17bb4 --- /dev/null +++ b/dot_local/bin/executable_omarchy-summon @@ -0,0 +1,55 @@ +#!/bin/bash + +# omarchy:summary=Launch a floating pinned app (youtube) or bring its window to the focused monitor's bottom-left corner. + +set -uo pipefail + +# Usage: omarchy-summon [youtube] +# +# Each profile is one app kept as a picture-in-picture window: the window +# rule in hypr/hyprland.lua floats, pins, sizes and parks it when it first +# opens, so a plain launch is enough when no window exists. When one does, +# this script carries it to the focused monitor and hands it to omarchy-pip +# to fit it there again: the two panels differ in logical size, and Hyprland +# keeps a floating window's pixel size and offset across a monitor move. +# +# PATTERN is an ERE over the window class. A web app's picture-in-picture +# overlay shares the class, so windows titled Picture-in-Picture are skipped +# (Omarchy's own pip rule owns those). +case ${1:-youtube} in +youtube) + # Chromium derives the class from the app URL, and the same web app has + # shown up under more than one host spelling (youtube.com vs + # www.youtube.com) once a video is playing, so match the family. + PATTERN='^brave-(www\.)?youtube\.com__.*$' + LAUNCH="gtk-launch YouTube" + ;; +*) + echo "usage: ${0##*/} [youtube]" >&2 + exit 2 + ;; +esac + +# Lua dispatcher first, classic name as the pre-lua fallback (same shape as +# the other omarchy-* scripts here). +hypr_dispatch() { + local lua="$1" + shift + hyprctl dispatch "$lua" >/dev/null 2>&1 || hyprctl dispatch "$@" >/dev/null +} + +ADDRESS=$(hyprctl clients -j | jq -r --arg re "$PATTERN" ' + first(.[] | select((.class | test($re)) and ((.title | test("Picture.?in.?[Pp]icture")) | not))) + | .address // empty') + +if [[ -z $ADDRESS ]]; then + exec setsid uwsm-app -- $LAUNCH >/dev/null 2>&1 +fi + +WINDOW="address:$ADDRESS" +MON_NAME=$(hyprctl monitors -j | jq -r '.[] | select(.focused) | .name') +[[ -n $MON_NAME ]] || exit 1 + +hypr_dispatch "hl.dsp.window.move({ monitor = \"$MON_NAME\", window = \"$WINDOW\" })" movewindow "mon:$MON_NAME,$WINDOW" +omarchy-pip on "$WINDOW" +hypr_dispatch "hl.dsp.focus({ window = \"$WINDOW\" })" focuswindow "$WINDOW" diff --git a/dot_local/bin/executable_omarchy-toggle-scratch b/dot_local/bin/executable_omarchy-toggle-scratch index abfce71..c69bbb4 100755 --- a/dot_local/bin/executable_omarchy-toggle-scratch +++ b/dot_local/bin/executable_omarchy-toggle-scratch @@ -4,7 +4,7 @@ set -uo pipefail -# Usage: omarchy-toggle-scratch [scratchpad|fastmail|beeper|youtube] +# Usage: omarchy-toggle-scratch [scratchpad|fastmail|beeper] # # Each profile is a special workspace, the monitor it opens on, and its # resident apps (class pattern -> launch command; the pattern is an anchored @@ -38,15 +38,8 @@ beeper) NAME="beeper" MEMBERS=("Beeper|gtk-launch beeper") ;; -youtube) - NAME="youtube" - # Chromium derives the class from the app URL, and the same web app has - # shown up under more than one host spelling (youtube.com vs - # www.youtube.com) once a video is playing, so match the family. - MEMBERS=("brave-(www\\.)?youtube\\.com__.*|gtk-launch YouTube") - ;; *) - echo "usage: ${0##*/} [scratchpad|fastmail|beeper|youtube]" >&2 + echo "usage: ${0##*/} [scratchpad|fastmail|beeper]" >&2 exit 2 ;; esac