scripts reorganize

This commit is contained in:
2026-10-06 22:34:05 +02:00
parent a2a056a68d
commit 0734bee219
60 changed files with 824 additions and 5 deletions

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# This script sends a quick notification through dunst, informing the user about the backlight of their screen
brightness=$(brightnessctl -m | awk -F, '{print $4}')
dunstify -u low \
-a Backlight \
-r 88 \
-i ~/.icons/candy-icons/apps/scalable/lightbeat.svg \
"Screen brightness is $brightness"

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env fish
# This script switches the wallpaper to a random image found under ~/Pictures/wallpapers
# Supports i3, Hyprland, and Sway.
switch "$XDG_CURRENT_DESKTOP"
case i3
feh --bg-fill (find ~/Pictures/wallpapers -type f -regextype posix-extended -iregex "/(.*/)*[^\.]*\.(png|jpe?g|webp)" | sort -R | head -n1)
case sway Hyprland
swaybg -i (find ~/Pictures/wallpapers -type f | sort -R | head -n1) &
set newpid $last_pid
sleep 0.2
for pid in (pgrep -x swaybg)
if test $pid -ne $last_pid
kill $pid
end
end
end

27
scripts/WMScripts/dunst.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# toggles dunstctl + sends a message (thru dounst ironically)
lvl=$(dunstctl get-pause-level)
send_msg() {
dunstify -u low \
-a Backlight \
-r 92 \
-i ~/.icons/candy-icons/apps/scalable/cliq.svg \
"Toggled dunst $1"
}
if [[ $lvl == "0" ]]; then
# dunst is currently enabled; send message first then disable
dunstctl close-all
# send_msg "off"
# sleep 2
dunstctl set-paused toggle
else
# dunst is currently disabled; enable then send message
dunstctl set-paused toggle
# send_msg "on"
fi

42
scripts/WMScripts/dunst_bar.py Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env nix-shell
#! nix-shell -i python3 --packages python314Packages.dbus-fast
from asyncio import run, Event
from json import dumps
from dbus_fast.aio import MessageBus
from dbus_fast.proxy_object import BaseProxyInterface, BaseProxyObject
def update_dbus_state(interface, changed, _) -> None:
if interface != 'org.dunstproject.cmd0':
return
if 'paused' in changed:
update_output(changed['paused'].value)
def update_output(paused: bool) -> None:
print(dumps({
"text": "󱙍" if paused else "󰍡",
"alt": "",
"tooltip": "",
"class": "muted-notifs" if paused else "unmuted-notifs",
"percentage": 0
}, ensure_ascii=False))
async def main() -> None:
bus = await MessageBus().connect()
introspection = await bus.introspect('org.freedesktop.Notifications', '/org/freedesktop/Notifications')
notifications: BaseProxyObject = bus.get_proxy_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications', introspection)
properties = notifications.get_interface('org.freedesktop.DBus.Properties')
dunst = notifications.get_interface('org.dunstproject.cmd0')
properties.on_properties_changed(update_dbus_state)
update_output(await dunst.get_paused())
await Event().wait()
if __name__ == "__main__":
run(main())

78
scripts/WMScripts/exit.sh Executable file
View File

@@ -0,0 +1,78 @@
#!/usr/bin/env sh
# This script cleanly exits a variety of window managers, depending on the given parameters
# Also checks no windows exist on the scratchpad (as I tend to forget about those)
case "$XDG_CURRENT_DESKTOP" in
"Hyprland")
lock="hyprlock"
cmd="hyprctl clients -j | jq . | grep -E 'special' -c"
ctl="hyprctl dispatch"
logout="uwsm stop"
;;
"sway")
lock="swaylock --screenshots --clock --indicator --indicator-radius 100 --indicator-thickness 7 --effect-pixelate 25 -- effect-vignette 0.5:0.5 --ring-color 244273a --key-hl-color c6a0f6 --line-color 00000000 --inside-color 00000000 --separator-color 00000000"
cmd="swaymsg -rt get_tree | jq . | grep -E 'scratchpad_state\": \"(changed|fresh)' -c"
ctl="swaymsg"
logout="swaymsg exit"
;;
"i3")
lock='i3lock -n -c 18192666 -k --radius 180 --inside-color=24273a --layout-color=cad3f5 --time-color=cad3f5 --date-color=cad3f5 --greeter-color=cad3f5 --verif-color=cad3f5 --wrong-color=cad3f5 --modif-color=cad3f5 --ring-color=c6a0f6 --insidever-color=24273a --ringver-color=8aadf4 --insidewrong-color=24273a --ringwrong-color=ed8796 --keyhl-color=a6da95 --bshl-color=f5a97f --separator-color=24273a --date-str="%A, %d %B %Y"'
cmd="i3-msg -t get_tree | jq . | grep -E 'scratchpad_state\": \"(changed|fresh)' -c"
ctl="i3-msg"
logout="i3-msg exit"
;;
*)
dunstify -u low -t 2000 -i ~/.icons/candy-icons/apps/scalable/system-shutdown.svg "This desktop is not supported."
exit
;;
esac
spec_exist() {
n=$(eval "$cmd")
if [ "$n" -gt 0 ]; then
[ "$n" -gt 1 ] && msg="$n windows" || msg="window"
dunstify -t 3000 \
-u low \
-i ~/.icons/candy-icons/apps/scalable/system-shutdown.svg \
-a i3exit \
"Close the $msg in the scratchpad first."
exit 0
fi
}
lock() {
dunstctl set-paused true
bash -c "$lock"
dunstctl set-paused false
}
case "$1" in
lock)
lock
;;
logout)
eval "$logout"
;;
suspend)
systemctl suspend && lock
;;
hibernate)
systemctl hibernate && lock
;;
reboot)
spec_exist
systemctl reboot
;;
shutdown)
spec_exist
systemctl poweroff
;;
*)
echo "Usage: $0 {lock|logout|suspend|hibernate|reboot|shutdown}"
exit 2
esac
exit 0

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env sh
# launch a new kitty window.
# If a kitty window is active, sets the new CWD to the current one.
# Else just launch a new one.
case "$XDG_CURRENT_DESKTOP" in
"Hyprland")
pid=$(hyprctl activewindow -j | jq '. | if .initialTitle == "kitty" then .pid else -1 end')
;;
*)
pid="-1"
;;
esac
if [[ "$pid" == "-1" ]]; then
kitty &
else
kitten @ --to unix:@kitty-$pid launch --cwd current --type os-window &
fi

18
scripts/WMScripts/microphone.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# This script updates the microphone custom module of my specific polybar setup
case "$XDG_CURRENT_DESKTOP" in
"sway"|"Hyprland")
"$(dirname "$0")/microphone_bar.sh" mute
kill -n36 $(pgrep waybar)
;;
"i3")
polybar-msg -p "$(xdotool search --name polybar-default getwindowpid)" action "#microphone.hook.0"
polybar-msg -p "$(xdotool search --name polybar-secondary getwindowpid)" action "#microphone.hook.1"
;;
*)
dunstify -u low -t 2000 -i ~/.icons/candy-icons/apps/scalable/pavucontrol.svg "Current desktop is not supported by microphone.sh"
exit
;;
esac

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
MUTE=" "
NORMAL=""
output () {
case "$XDG_CURRENT_DESKTOP" in
"sway"|"Hyprland")
if [[ -n "$1" ]]; then
echo "{\"text\": \"$NORMAL\", \"alt\": \"\", \"tooltip\": \"\", \"class\": \"unmuted-mike\", \"percentage\": 0}"
else
echo "{\"text\": \"$MUTE\", \"alt\": \"\", \"tooltip\": \"\", \"class\": \"muted-mike\", \"percentage\": 0}"
fi
;;
"i3")
if [[ -n "$1" ]]; then
echo "$NORMAL"
else
echo "%{F#707880}$MUTE%{T- F-}"
fi
;;
*)
dunstify -t 2000 -u low -i ~/.icons/candy-icons/apps/scalable/noisetorch.svg "This desktop is not supported."
exit
;;
esac
}
if [[ "$1" == "mute" ]]; then
amixer sset Capture toggle > /dev/null
fi
STATUS=$(amixer get Capture | tail -n1 | awk '{print $5}' -)
case $STATUS in
"[on]")
output "yes"
;;
*)
output
;;
esac

28
scripts/WMScripts/murder.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# This script allows the user to select a window
# then nicely asks it to shut down (SIGTERM).
# if the window does not shut down, it is murdered instead (SIGKILL)
case "$XDG_CURRENT_DESKTOP" in
"Hyprland")
id=$(hyprprop | jq '.pid')
;;
"sway")
id=$(wlprop | jq '.pid')
;;
"i3")
id=$(xprop _NET_WM_PID | awk '{print $3}')
;;
*)
dunstify -u low -t 2000 -i ~/.icons/candy-icons/apps/scalable/clamav.svg "This session type is not supported by murder.sh"
exit
esac
[[ ! "$id" =~ [0-9]+ ]] && exit
kill "$id"
sleep 2
if [[ $(ps -p "$id" 2>&1 > /dev/null) -ne 0 ]]; then
kill -s 9 "$id"
fi

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# mutes the pipewire sink of the active window, if we can find it.
# $1: if non-empty, applies the specified shader
# 1: get activewindow pid
# 2 match to pactl source
# 2a: try directly against pid
# 2b: if fails: run through fallbacks for specific windows
# 3 toggle pactl source mute
# 4 set window tag for custom shading
# Toggle mute a pactl sink
# $1 int: id of sink to toggle
function toggle_sink {
pactl set-sink-input-mute "$1" toggle
}
# Toggle mute for an array of pactl sinks
# $1 string: space separated list of sink ids
function toggle_multiple {
for i in $1; do
toggle_sink "$i"
done
}
case "$XDG_CURRENT_DESKTOP" in
"Hyprland")
activePid=$(hyprctl activewindow -j | jq ".pid")
tagCmd="hyprctl dispatch 'hl.dsp.window.tag({ tag = \"shader/$1\" })'"
class=$(hyprctl activewindow -j | jq ".class")
;;
*)
dunstify -u low -t 2000 -i ~/.icons/candy-icons/apps/scalable/yast-sound.svg "muteActive does not support the current desktop."
exit
;;
esac
sinks=$(pactl -f json list sink-inputs)
binary=$(ps -q "$activePid" -o comm=)
mutableSinks=$(printf "%s" "$sinks" | jq -r ".[] | select(.properties.\"application.process.id\" == \"$activePid\") | .index")
# fallbacks
[[ -z "$mutableSinks" ]] && mutableSinks=$(printf "%s" "$sinks" | jq -r ".[] | select(.properties.\"application.process.binary\" == \"$binary\") | .index") # Feishin
[[ -z "$mutableSinks" ]] && mutableSinks=$(printf "%s" "$sinks" | jq -r ".[] | select(.properties.\"application.id\" == \"$binary\") | .index") # mpv
[[ -z "$mutableSinks" && "$class" = \"tf_linux64\" ]] && mutableSinks=$(printf "%s" "$sinks" | jq -r ".[] | select(.properties.\"application.name\" == \"tf_linux64\" ) .index") # tf2
if [[ -z "$mutableSinks" ]]; then
# still no matched sink; give up
printf "Couldn't match activeWindow to any pipewire sinks!\n"
dunstify -u low \
-a Mute \
-r 35 \
-i ~/.icons/candy-icons/apps/scalable/yast-sound.svg \
"Could not match active window to pipewire input"
exit 1
else
printf "Matched activeWindow | class | %s\n |binary| %s\n | sink | %s | to pipewire sink" "$class" "$binary" "$mutableSinks" | column -ts '|'
dunstify -u low \
-a Mute \
-r 35 \
-i ~/.icons/candy-icons/apps/scalable/yast-sound.svg \
"Toggled mute on active window"
fi
toggle_multiple "$mutableSinks"
[ -n "$1" ] && eval "$tagCmd"

75
scripts/WMScripts/record.sh Executable file
View File

@@ -0,0 +1,75 @@
#!/usr/bin/env bash
# This script allows one to record a slected area of the screen.
# Calling the script again will end recording, and save the file to
# ~/Videos/screen captures/$(date +%F_%T).mp4
# create directory if necessary
mkdir -p ~/Videos/screen\ captures
# variables
record_file="/tmp/record_file"
now="$(date +%F_%T)"
file="$HOME/Videos/screen captures/$now.mp4"
stop_recording () {
kill -2 "$(head -1 $record_file)"
kill -KILL "$(head -2 $record_file | tail -n1 )"
old_date="$(cat $record_file | tail -n1)"
rm "$record_file"
# notif
dunstify -u low \
-a Record \
-r 8827 \
-i ~/.icons/candy-icons/apps/scalable/simplescreenrecorder.svg \
"Saved recording to $old_date.mp4"
echo "Saved recording to $old_date.mp4"
}
start_recording () {
case "$XDG_SESSION_TYPE" in
x11)
# get coordinates
slop=$(slop -f "%x %y %w %h %g %i") || exit 1
read -r X Y W H G ID <<< "$slop"
# record screen
ffmpeg -v 1 -r 30 -f x11grab -s "$W"x"$H" -i :0.0+"$X","$Y" -show_region 1 -vf "scale=2*trunc(iw/2):2*trunc(ih/2)" -vcodec libx264 -pix_fmt yuv420p -preset ultrafast -crf 18 -threads 0 "$file" -y &
;;
wayland)
# get coordinates
if ! slurp="$(slurp)"; then
exit
fi
wf-recorder -g "$slurp" -f "$file" --framerate 30 &
;;
*)
dunstify -t 2000 -u low -i ~/.icons/candy-icons/apps/scalable/camorama.svg "This session type is not supported by record.sh"
exit
;;
esac
# print metadata to tmp file
echo $! > "$record_file"
echo $$ >> "$record_file"
echo "$now" >> "$record_file"
# loop notif
i=0
while :; do
i=$(($i + 1))
dunstify -u low \
-a Record \
-r 8827 \
-i ~/.icons/candy-icons/apps/scalable/simplescreenrecorder.svg \
"Recording for $(date -d@$i -u +%H:%M:%S)"
sleep 1
done
}
[[ -e "$record_file" ]] && stop_recording || start_recording

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# This script allows the user to make a screenshot (through flameshot)
# and will then open said screenshot from the clipboard into feh
# it will also make the feh window floating
case "$XDG_CURRENT_DESKTOP" in
"Hyprland")
paste="wl-paste -t image/png"
move () {
bash -c "hyprctl dispatch 'hl.dsp.window.move({ x = \"$1\", y = \"$2\", relative = false})'"
}
;;
"sway")
paste="wl-paste -t image/png"
move () {
bash -c "swaymsg move absolute position $1 $2"
}
;;
"i3")
paste="xclip -selection clipboard -t image/png -o"
move () {
bash -c "i3-msg move position $1 $2"
}
;;
*)
dunstify -t 2000 -u low -i ~/.icons/candy-icons/apps/scalable/image-viewer.svg "Current desktop is not supported by floatshot."
exit
;;
esac
out=$(flameshot gui -g)
# If the capture was cancelled, out will be empty
if [[ -n "$out" ]]; then
# get x,y of selection
x=$(echo "$out" | awk -F'+' '{print $2}')
y=$(echo "$out" | awk -F'+' '{print $3}')
bash -c "$paste" | feh - -T notitle --class 'floatme' &
pid="$!"
while [[ -z $(wmctrl -lp | awk -vpid=$pid '$3==pid {print $1; exit}') ]]; do
sleep 0.05
done
move "$x" "$y"
fi

36
scripts/WMScripts/tess.sh Executable file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env bash
# This script allows the user to make a selection, then runs that selection through OCR and copies the result to clipboard
case "$XDG_SESSION_TYPE" in
"wayland")
copy="wl-copy"
screenshot="grim -g"
slop=$(slurp -f "%x,%y %wx%h") || exit 1
;;
"x11")
copy="xclip -selection c"
screenshot="import -window root -crop "
slop=$(slop -f "%G") || exit 1
;;
*)
dunstify -t 2000 -u low -i ~/.icons/candy-icons/apps/scalable/code.svg "Current session type not supported by tess.sh"
exit
;;
esac
# files
imagefile="/tmp/tesseract.$RANDOM.png"
text="/tmp/translation"
# get screenshot
read -r G <<< "$slop"
bash -c "$screenshot \"$G\" $imagefile"
# OCR, save to clipboard
tesseract $imagefile $text 2>/dev/null
cat "$text.txt" | bash -c "$copy"
# cleanup
rm $text".txt"
rm "$imagefile"

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# This script updates the clementine and combine toggle custom modules of my specific polybar setup
case "$XDG_CURRENT_DESKTOP" in
"sway"|"Hyprland")
case "$1" in
"clementine")
"$(dirname "$0")/toggle_audiocables_bar.sh" toggle clementine
kill -37 $(pgrep waybar)
;;
"combine")
"$(dirname "$0")/toggle_audiocables_bar.sh" toggle combine
kill -37 $(pgrep waybar)
;;
*)
echo "Unknown sink specified."
;;
esac
;;
"i3")
case "$1" in
"clementine")
polybar-msg -p "$(xdotool search --name polybar-default getwindowpid)" action "#audiocables.hook.0"
;;
"combine")
polybar-msg -p "$(xdotool search --name polybar-default getwindowpid)" action "#audiocables.hook.1"
;;
*)
echo "unknown sink specified."
;;
esac
;;
*)
dunstify -u low -t 2000 -i ~/.icons/candy-icons/apps/scalable/pavucontrol.svg "Current desktop is not supported by audiocables.sh"
exit
;;
esac

View File

@@ -0,0 +1,112 @@
#!/usr/bin/env bash
# This script toggles state between a few of my audio cables:
# combine sink -> default_output | hyperx_output
# clementine sink -> combine sink | default_output
# The script also outputs a symbol indicating which configuration is active.
# Usecase: My USB-C headset is a different sink than the one in the laptop; sometimes I want to have audio over the headset, sometimes through the laptop.
# Output symbols
# SYMBOLS=("neither" "co^!cl" "cl^!co" "both")
SYMBOLS=("󰴸 " "󰝚 " "󰓃 " "󰋋 ")
# The sinks to test against
DEFAULT_SINKS=("bluez_output_internal.0A_E6_E0_F9_F1_BE.1" "alsa_output.pci-0000_06_00.6.analog-stereo" "bluez_output.20_18_5B_F0_FC_4A.1" "bluez_output.0A_E6_E0_F9_F1_BE.1" "bluez_output.0A_E6_E0_F9_F1_BE" "bluez_output.0A:E6:E0:F9:F1:BE")
USB_C_SINK="alsa_output.usb-HP__Inc_HyperX_Cloud_III_Wireless_0000000000000000-00.analog-stereo"
COMBINE_SINK="combine"
# awkcommand - a little complicated, but basically tests if the DEFAULT_SINK is connected to the clementine sink (adding +2 if so) or the combine sink (adding +1). This gives a numerical mapping for all 4 states im interested in:
# 0: neither combine,clementine are attached to usb_c_sink
# 1: only combine is attached to usb_c_sink
# 2: only clementine is attached to usb_c_sink
# 3: both combine,clementine are attached to usb_c_sink
read -r -d "" awkcommand << EOF
/$USB_C_SINK:playback_FL/,/$USB_C_SINK:playback_FR/ {
if (\$2 == "combine:monitor_FL") {
state += 1
}
}
/$COMBINE_SINK:playback_FL/,/$COMBINE_SINK:playback_FR/ {
if (\$2 == "clementine:monitor_FL") {
state += 2
}
}
END {
printf "%d\n", state
}
EOF
# Attaches a single sink to a specific source
function attach() {
echo "attaching $1 to $2"
pw-link "$1":monitor_FL "$2":playback_FL
pw-link "$1":monitor_FR "$2":playback_FR
}
# Attaches an array of sinks to a specific source.
# $1 = source (pw monitor)
# $2 = list of sinks to attach
function attachArray() {
name=$2[@]
a=("${!name}")
for sink in "${a[@]}"; do
attach "$1" "$sink"
done
}
# Detaches a single sink from a specific source
function detach() {
echo "detaching $1 from $2"
pw-link -d "$1":monitor_FL "$2":playback_FL
pw-link -d "$1":monitor_FR "$2":playback_FR
}
# Detaches an array of sinks to a specific source.
# $1 = source (pw monitor)
# $2 = list of sinks to attach
function detachArray() {
name=$2[@]
a=("${!name}")
for sink in "${a[@]}"; do
detach "$1" "$sink"
done
}
state=$(pw-link -li | awk "$awkcommand")
IFS=$'\n'
# actually toggle iff we have a $1 telling us to
if [[ "$1" == "toggle" ]];then
case "$2" in
"clementine")
if [[ $state -eq 2 || $state -eq 3 ]]; then
detach "clementine" "$COMBINE_SINK"
attachArray "clementine" "DEFAULT_SINKS"
state=$((state-2))
else
detachArray "clementine" "DEFAULT_SINKS"
attach "clementine" "$COMBINE_SINK"
state=$((state+2))
fi
;;
"combine")
if [[ $state -eq 1 || $state -eq 3 ]]; then
detach "combine" "$USB_C_SINK"
attachArray "combine" "DEFAULT_SINKS"
state=$((state-1))
else
detachArray "combine" "DEFAULT_SINKS"
attach "combine" "$USB_C_SINK"
state=$((state+1))
fi
;;
*)
echo "Unknown option: '$2'"
;;
esac
fi
echo "${SYMBOLS[$state]}"