#!/usr/bin/env bash # # The MSN Messenger nudge: the whole screen shakes for a moment. # # Done with a Hyprland screen shader that moves the whole picture to a new # random offset `speed` times a second, applied for `duration` seconds and # then taken away again. Damage tracking is switched off for those seconds # so a frame is a full redraw; it is put back exactly as it was. (Hyprland # still only draws when something changed: the service shows a 2px overlay # that repaints every frame while this runs, see FrameTicker.qml.) # # Options (set on the effect in rules.json): # duration seconds, default 1 # intensity 0.5..30, how far the picture moves, default 1.5 (a phone buzz; 6 is a proper MSN nudge) # speed new positions per second, default 200: at or above the refresh rate every frame differs set -uo pipefail duration=${AR_OPT_DURATION:-1} intensity=${AR_OPT_INTENSITY:-1.5} speed=${AR_OPT_SPEED:-200} num='^[0-9]*\.?[0-9]+$' [[ $duration =~ $num ]] || duration=1 [[ $intensity =~ $num ]] || intensity=1.5 [[ $speed =~ $num ]] || speed=200 command -v hyprctl >/dev/null 2>&1 || { echo "nudge: hyprctl not found" >&2; exit 1; } run_dir=${XDG_RUNTIME_DIR:-/tmp}/attention-required mkdir -p "$run_dir" || exit 1 shader=$run_dir/nudge.frag # One nudge at a time. A second one arriving mid-shake is dropped rather # than queued: by the time it ran, the first would have made the point. exec 9>"$run_dir/nudge.lock" flock -n 9 || exit 0 amp=$(awk -v i="$intensity" 'BEGIN { printf "%.5f", (i > 30 ? 30 : i) / 1000 }') rate=$(awk -v s="$speed" 'BEGIN { printf "%.1f", (s < 1 ? 1 : s) }') cat > "$shader" </dev/null 2>&1; then return 0 fi local plain=$value [[ $plain == \"*\" ]] && plain=${plain:1:-1} hyprctl -q keyword "$conf_key" "$plain" >/dev/null 2>&1 } prev_shader=$(hyprctl -j getoption decoration:screen_shader 2>/dev/null | jq -r '.str // empty') [[ -z $prev_shader || $prev_shader == "$shader" ]] && prev_shader='[[EMPTY]]' prev_damage=$(hyprctl -j getoption debug:damage_tracking 2>/dev/null | jq -r '.int // 2') [[ $prev_damage =~ ^[0-9]+$ ]] || prev_damage=2 restore() { set_option decoration screen_shader decoration:screen_shader "\"$(lua_string "$prev_shader")\"" set_option debug damage_tracking debug:damage_tracking "$prev_damage" } trap restore EXIT INT TERM set_option debug damage_tracking debug:damage_tracking 0 set_option decoration screen_shader decoration:screen_shader "\"$(lua_string "$shader")\"" sleep "$duration"