#!/bin/sh
# alloy-shot — take a screenshot and say that it worked.
#
# The four Print binds in the sway config used to be four inline grim calls.
# They worked, and they were reported as broken anyway, on 2026-07-29, by the
# person who wrote them: pressing Print produced a correct capture and no
# notification, no OSD, no shutter, no window. Nothing at all. The only way to
# learn it had worked was to go and look in the directory, which is
# indistinguishable from a dead keybind. So this exists for the notification
# more than for the capture.
#
# Three things moved in here that were wrong or fragile inline, and are the
# reason this is a script rather than `&& notify-send` appended four times:
#
#  1. The timestamp is taken ONCE. The config's `set $shot` held a literal
#     `$(date ...)`, expanded by the shell sway's `exec` runs it through. An
#     appended notification naming `$shot` would have expanded it a second time,
#     so any capture that straddled a second boundary would have announced a
#     filename that does not exist. A notification that lies about the path is
#     worse than no notification, and it would have been intermittent.
#  2. Cancelling a region select is not a failure. `slurp` exits non-zero when
#     Escape is pressed, which inline made `grim -g ""` run and fail; with a
#     notification appended it would have reported an error for a deliberate
#     cancel. Here it exits quietly.
#  3. One notification, one wording, one place to change it.
#
# Modes rather than four scripts because the notification and the timestamp are
# the shared part, which is exactly what was missing.

set -eu

# XDG_PICTURES_DIR is what xdg-user-dirs writes and what a localized install
# actually uses; ~/Pictures is the fallback for a session that has no user-dirs
# file yet. The sway config also `mkdir -p`s this at startup, and it is repeated
# here because this script is reachable without that having run: from a bind on
# a hand-edited config, from a shell, or on a first login where the exec order
# is not something to depend on.
dir="${XDG_PICTURES_DIR:-$HOME/Pictures}/Screenshots"
mkdir -p "$dir"

shot="$dir/screenshot-$(date +%Y%m%d-%H%M%S).png"

# Told, not guessed: a bind that names a mode this does not know is a typo in
# the config, and it should say so rather than silently capture the whole
# screen.
mode="${1:-}"
case "$mode" in
    output)
        grim "$shot"
        ;;
    region)
        # `|| exit 0` covers the Escape case above. It also swallows a real
        # slurp failure, which is the accepted cost: from here the two are the
        # same non-zero exit, and treating a cancel as an error is the louder
        # mistake.
        geometry="$(slurp)" || exit 0
        grim -g "$geometry" "$shot"
        ;;
    window)
        # The focused node's rect, in grim's `x,y WxH` geometry. jq is a stated
        # dependency of the image for exactly this line; see the Containerfile's
        # package list.
        geometry="$(swaymsg -t get_tree \
            | jq -r '.. | select(.focused?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"')"
        grim -g "$geometry" "$shot"
        ;;
    annotate)
        # Annotate the most recent capture rather than taking a new one, which
        # is the bind's whole point: shoot first, mark it up after. Nothing to
        # notify about, because satty opens a window and is its own feedback.
        #
        # `ls -t` on a glob that matches nothing exits non-zero under `set -e`,
        # so an empty directory ends here instead of running satty on an empty
        # filename.
        latest="$(ls -t "$dir"/*.png 2>/dev/null | head -1)" || exit 0
        [ -n "$latest" ] || exit 0
        exec satty --filename "$latest"
        ;;
    *)
        echo "alloy-shot: expected one of output, region, window, annotate" >&2
        exit 2
        ;;
esac

# Past here the capture is on disk. The notification is the point of this
# script, so a notification daemon that is not running is worth one line on
# stderr rather than a failed exit: the screenshot was taken either way, and
# `set -e` would otherwise turn a missing daemon into what looks like a failed
# capture.
notify-send -a alloy -i camera-photo "Screenshot saved" "$shot" \
    || echo "alloy-shot: saved $shot, but the notification could not be sent" >&2
