Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
lainiwa committed Dec 17, 2023
1 parent 4cab4bd commit 788f71f
Showing 1 changed file with 114 additions and 67 deletions.
181 changes: 114 additions & 67 deletions screenshot
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@
# * mode: full|selected|focused
#
# ENVIRONMENT VARIABLES:
# * SCREENSHOT_DOCKER_HOST: set to prefer using docker for image optimization
# * LAINTOOLS_SS_DOCKER_HOST: set to prefer using docker for image optimization
# Examples: `unix:///var/run/docker.sock` for local execution
# `ssh://[email protected]` for remote
#
# DEPENDENCIES:
# * Interpreter: POSIX shell + coreutils
# * Screenshot tool:
# * menyoki + xdotool + (hacksaw | xrectsel | slop)
# * shotgun + xdotool + (hacksaw | xrectsel | slop)
# * maim + xdotool
# * escrotum + xdotool
# * mss + xdotool + (hacksaw | xrectsel | slop)
# * menyoki + (xdotool | xprop) + (hacksaw | xrectsel | slop)
# * shotgun + (xdotool | xprop) + (hacksaw | xrectsel | slop)
# * maim + (xdotool | xprop)
# * escrotum + (xdotool | xprop)
# * mss + (xdotool | xprop) + (hacksaw | xrectsel | slop)
# * scrot
# * (graphicsmagick | imagemagick) + xdotool
# * (graphicsmagick | imagemagick) + (xdotool | xprop)
# * Clipboard tool [opt]: xclip | xsel
# * Image lossless optimizer [opt]: oxipng | ect | leanify | pngout | optipng | pngcrush | advpng | jpegoptim | jpegtran
# * OCR tool [opt]: tesseract + tesseract-ocr-rus | easyocr
# * Image lossless optimizer [opt]: oxipng | ect | leanify | pngout | optipng | pngcrush | advpng
# * OCR tool [opt]: tesseract [+ tesseract-ocr-*] | easyocr
# * Locks manager [opt]: flock
#

Expand All @@ -35,32 +35,78 @@ set -o errexit # exit on fail
set -o nounset # exit on undeclared variable
# set -o xtrace # trace execution

XDG_DATA_HOME=${XDG_DATA_HOME:-$HOME/.local/share}

SCREENSHOT_DIR=${SCREENSHOT_DIR:-${HOME}/screenshots}
LAINTOOLS_SS_DIR=${LAINTOOLS_SS_DIR:-${HOME}/screenshots}
save_date=$(date '+%s_%Y.%m.%d_%H:%M:%S')
SCREENSHOT_NAME=${SCREENSHOT_NAME:-${save_date}.png}
SCREENSHOT_PATH=${SCREENSHOT_PATH:-${SCREENSHOT_DIR}/${SCREENSHOT_NAME}}
SCREENSHOT_OCR_LANGS=${SCREENSHOT_OCR_LANGS:-eng+rus}
LAINTOOLS_SS_NAME=${LAINTOOLS_SS_NAME:-${save_date}.png}
LAINTOOLS_SS_PATH=${LAINTOOLS_SS_PATH:-${LAINTOOLS_SS_DIR}/${LAINTOOLS_SS_NAME}}
LAINTOOLS_SS_OCR_LANGS=${LAINTOOLS_SS_OCR_LANGS:-eng+rus}

LAINTOOLS_DRBTT_STORAGE_PATH=${LAINTOOLS_DRBTT_STORAGE_PATH:-$XDG_DATA_HOME/LAINTOOLS/drbtt_events.duckdb}

# We expect word splitting here
# shellcheck disable=2046
get_size() { set -- $(ls -dn "$1") && printf %s "$5"; }
has() { command -v "$@" >/dev/null; }
has_focus_window_getter() { has xdotool || has xprop sed; }
has_selection_getter() { has hacksaw || has xrectsel || has slop; }
was_optimized() { printf "%s\n" "${SCREENSHOT_NAME}" >> "${SCREENSHOT_DIR}/.optimized"; }
was_optimized() { printf "%s\n" "${LAINTOOLS_SS_NAME}" >> "${LAINTOOLS_SS_DIR}/.optimized"; }

get_focused_window() {
get_focused_window_id() {
if has xdotool; then
xdotool getwindowfocus -f
elif has xprop sed; then
printf '%d\n' "$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"
xprop -notype -root 32i ' $0' _NET_ACTIVE_WINDOW | cut -d ' ' -f 2
else
printf "Internal error!\n" >&2
exit 1
fi
}

init_storage() {
mkdir -p "$(dirname "${LAINTOOLS_DRBTT_STORAGE_PATH}")"
duckdb "${LAINTOOLS_DRBTT_STORAGE_PATH}" "
CREATE TABLE IF NOT EXISTS events(
timestamp TIMESTAMPTZ PRIMARY KEY,
instance VARCHAR,
class VARCHAR,
name VARCHAR,
cursor_x USMALLINT,
cursor_y USMALLINT,
);
"
}

sql_escape() {
# src: https://github.com/larkery/zsh-histdb/blob/30797f0c50c31c8d8de32386970c5d480e5ab35d/sqlite-history.zsh#L24
# print -r -- ${${@//\'/\'\'}//$'\x00'}
printf %s "${1}" |sed -e 's/\\/\\\\/g' -e "s/'/\\\'/g"
}

snapshot_event() (
TIMESTAMP="$(date --utc --iso-8601=seconds)"
eval "$(xprop -id "$(get_focused_window_id)" -notype '=$0\n' WM_NAME '=$1\nWM_INSTANCE=$0\n' WM_CLASS)"
eval "$(xdotool getmouselocation --shell)"
duckdb "${LAINTOOLS_DRBTT_STORAGE_PATH}" "
INSERT INTO events VALUES (
'$(sql_escape "${TIMESTAMP}")',
'$(sql_escape "${WM_INSTANCE}")',
'$(sql_escape "${WM_CLASS}")',
'$(sql_escape "${WM_NAME}")',
$(sql_escape "${X}"),
$(sql_escape "${Y}"),
);
"
)

drbttd() {
init_storage
while sleep 1; do
snapshot_event || true
done
}

usage() {
printf "\
Usage: screenshot COMMAND
Expand Down Expand Up @@ -102,9 +148,9 @@ make_screenshot() {
fi
)
case $1 in
'full' ) menyoki -q capture --root png save -- "${SCREENSHOT_PATH}" ;;
'select' ) menyoki -q capture --root --size "${selection}" png save -- "${SCREENSHOT_PATH}" ;;
'focused') menyoki -q capture --focus png save -- "${SCREENSHOT_PATH}" ;;
'full' ) menyoki -q capture --root png save -- "${LAINTOOLS_SS_PATH}" ;;
'select' ) menyoki -q capture --root --size "${selection}" png save -- "${LAINTOOLS_SS_PATH}" ;;
'focused') menyoki -q capture --focus png save -- "${LAINTOOLS_SS_PATH}" ;;
esac

elif has_focus_window_getter && has_selection_getter && has shotgun; then
Expand All @@ -119,23 +165,23 @@ make_screenshot() {
)
# shellcheck disable=SC2086
case $1 in
'full' ) shotgun -- "${SCREENSHOT_PATH}" ;;
'select' ) shotgun ${selection} -- "${SCREENSHOT_PATH}" ;;
'focused') shotgun --id "$(get_focused_window)" -- "${SCREENSHOT_PATH}" ;;
'full' ) shotgun -- "${LAINTOOLS_SS_PATH}" ;;
'select' ) shotgun ${selection} -- "${LAINTOOLS_SS_PATH}" ;;
'focused') shotgun --id "$(get_focused_window_id)" -- "${LAINTOOLS_SS_PATH}" ;;
esac

elif has_focus_window_getter && has maim; then
case $1 in
'full' ) maim -- "${SCREENSHOT_PATH}" ;;
'select' ) maim --select -- "${SCREENSHOT_PATH}" ;;
'focused') maim --window "$(get_focused_window)" -- "${SCREENSHOT_PATH}" ;;
'full' ) maim -- "${LAINTOOLS_SS_PATH}" ;;
'select' ) maim --select -- "${LAINTOOLS_SS_PATH}" ;;
'focused') maim --window "$(get_focused_window_id)" -- "${LAINTOOLS_SS_PATH}" ;;
esac

elif has_focus_window_getter && has escrotum; then
case $1 in
'full' ) escrotum -- "${SCREENSHOT_PATH}" ;;
'select' ) escrotum --select -- "${SCREENSHOT_PATH}" ;;
'focused') escrotum --xid "$(get_focused_window)" -- "${SCREENSHOT_PATH}" ;;
'full' ) escrotum -- "${LAINTOOLS_SS_PATH}" ;;
'select' ) escrotum --select -- "${LAINTOOLS_SS_PATH}" ;;
'focused') escrotum --xid "$(get_focused_window_id)" -- "${LAINTOOLS_SS_PATH}" ;;
esac

elif has_focus_window_getter && has_selection_getter && has mss; then
Expand All @@ -149,30 +195,30 @@ make_screenshot() {
fi
)
case $1 in
'full' ) mss --output "${SCREENSHOT_PATH}" ;;
'select' ) mss --coordinates "${selection}" --output "${SCREENSHOT_PATH}" ;;
'focused') mss --coordinates "$(get_focused_window)" --output "${SCREENSHOT_PATH}" ;;
'full' ) mss --output "${LAINTOOLS_SS_PATH}" ;;
'select' ) mss --coordinates "${selection}" --output "${LAINTOOLS_SS_PATH}" ;;
'focused') mss --coordinates "$(get_focused_window_id)" --output "${LAINTOOLS_SS_PATH}" ;;
esac

elif has scrot; then
case $1 in
'full' ) scrot --multidisp -- "${SCREENSHOT_PATH}" ;;
'select' ) scrot --select -- "${SCREENSHOT_PATH}" ;;
'focused') scrot --focused -- "${SCREENSHOT_PATH}" ;;
'full' ) scrot --multidisp -- "${LAINTOOLS_SS_PATH}" ;;
'select' ) scrot --select -- "${LAINTOOLS_SS_PATH}" ;;
'focused') scrot --focused -- "${LAINTOOLS_SS_PATH}" ;;
esac

elif has_focus_window_getter && has gm; then
case $1 in
'full' ) gm import -window root "${SCREENSHOT_PATH}" ;;
'select' ) gm import "${SCREENSHOT_PATH}" ;;
'focused') gm import -window "$(get_focused_window)" "${SCREENSHOT_PATH}" ;;
'full' ) gm import -window root "${LAINTOOLS_SS_PATH}" ;;
'select' ) gm import "${LAINTOOLS_SS_PATH}" ;;
'focused') gm import -window "$(get_focused_window_id)" "${LAINTOOLS_SS_PATH}" ;;
esac

elif has_focus_window_getter && has import; then
case $1 in
'full' ) import -window root -- "${SCREENSHOT_PATH}" ;;
'select' ) import -- "${SCREENSHOT_PATH}" ;;
'focused') import -window "$(get_focused_window)" -- "${SCREENSHOT_PATH}" ;;
'full' ) import -window root -- "${LAINTOOLS_SS_PATH}" ;;
'select' ) import -- "${LAINTOOLS_SS_PATH}" ;;
'focused') import -window "$(get_focused_window_id)" -- "${LAINTOOLS_SS_PATH}" ;;
esac

else
Expand All @@ -187,21 +233,21 @@ make_screenshot() {

copy_to_clipboard() {
if has xclip; then
printf %s "${SCREENSHOT_PATH}" | xclip -in -selection primary
xclip -selection clipboard -target image/png <"${SCREENSHOT_PATH}"
printf %s "${LAINTOOLS_SS_PATH}" | xclip -in -selection primary
xclip -selection clipboard -target image/png <"${LAINTOOLS_SS_PATH}"

elif has xsel; then
printf %s "${SCREENSHOT_PATH}" | xsel --input --primary
printf %s "${SCREENSHOT_PATH}" | xsel --input --clipboard
printf %s "${LAINTOOLS_SS_PATH}" | xsel --input --primary
printf %s "${LAINTOOLS_SS_PATH}" | xsel --input --clipboard
fi
}


# https://wiki.archlinux.org/title/List_of_applications/Multimedia#Console
optimize_image() {
if [ -n "${SCREENSHOT_DOCKER_HOST:-}" ] && docker info >/dev/null 2>&1; then
if [ -n "${LAINTOOLS_SS_DOCKER_HOST:-}" ] && docker info >/dev/null 2>&1; then
(
export DOCKER_HOST=${SCREENSHOT_DOCKER_HOST}
export DOCKER_HOST=${LAINTOOLS_SS_DOCKER_HOST}
# Create a container that would auto-destroy in 10 minutes
container_id=$(
docker run --rm -d \
Expand All @@ -211,43 +257,43 @@ optimize_image() {
# Destroy container on exit from brackets
trap 'docker rm -f "${container_id}" >/dev/null; trap - EXIT; exit' EXIT INT HUP
# Copy file to container, process it, and copy back
docker cp "${SCREENSHOT_PATH}" "${container_id}:/${SCREENSHOT_NAME}"
docker exec -ti "${container_id}" ect -9 "/${SCREENSHOT_NAME}"
docker container cp "${container_id}:/${SCREENSHOT_NAME}" "${SCREENSHOT_PATH}"
docker cp "${LAINTOOLS_SS_PATH}" "${container_id}:/${LAINTOOLS_SS_NAME}"
docker exec -ti "${container_id}" ect -9 "/${LAINTOOLS_SS_NAME}"
docker container cp "${container_id}:/${LAINTOOLS_SS_NAME}" "${LAINTOOLS_SS_PATH}"

was_optimized
)

elif has oxipng; then
oxipng --quiet --opt max --threads 1 -- "${SCREENSHOT_PATH}"
oxipng --quiet --opt max --threads 1 -- "${LAINTOOLS_SS_PATH}"
was_optimized

elif has etc; then
ect -9 "${SCREENSHOT_PATH}"
ect -9 "${LAINTOOLS_SS_PATH}"
was_optimized

elif has leanify; then
leanify --iteration 100 -- "${SCREENSHOT_PATH}"
leanify --iteration 100 -- "${LAINTOOLS_SS_PATH}"
was_optimized

elif has pngout; then
pngout "${SCREENSHOT_PATH}" "/tmp/${SCREENSHOT_NAME}" || [ $? = 2 ]
mv "/tmp/${SCREENSHOT_NAME}" "${SCREENSHOT_PATH}"
pngout "${LAINTOOLS_SS_PATH}" "/tmp/${LAINTOOLS_SS_NAME}" || [ $? = 2 ]
mv "/tmp/${LAINTOOLS_SS_NAME}" "${LAINTOOLS_SS_PATH}"
was_optimized

elif has optipng; then
optipng -o7 -strip all -- "${SCREENSHOT_PATH}"
optipng -o7 -strip all -- "${LAINTOOLS_SS_PATH}"
was_optimized

elif has pngcrush; then
pngcrush -brute -reduce "${SCREENSHOT_PATH}" "/tmp/${SCREENSHOT_NAME}"
if [ "$(get_size "/tmp/${SCREENSHOT_NAME}")" -lt "$(get_size "${SCREENSHOT_PATH}")" ]; then
mv "/tmp/${SCREENSHOT_NAME}" "${SCREENSHOT_PATH}"
pngcrush -brute -reduce "${LAINTOOLS_SS_PATH}" "/tmp/${LAINTOOLS_SS_NAME}"
if [ "$(get_size "/tmp/${LAINTOOLS_SS_NAME}")" -lt "$(get_size "${LAINTOOLS_SS_PATH}")" ]; then
mv "/tmp/${LAINTOOLS_SS_NAME}" "${LAINTOOLS_SS_PATH}"
fi
was_optimized

elif has advpng; then
advpng --recompress --shrink-insane --iter=100 -- "${SCREENSHOT_PATH}"
advpng --recompress --shrink-insane --iter=100 -- "${LAINTOOLS_SS_PATH}"
was_optimized

fi
Expand All @@ -257,23 +303,23 @@ optimize_image() {
ocr_image() {
if has tesseract; then
tesseract \
-l "${SCREENSHOT_OCR_LANGS}" \
-l "${LAINTOOLS_SS_OCR_LANGS}" \
--psm 1 \
"${SCREENSHOT_PATH}" \
"${SCREENSHOT_DIR}/${save_date}"
"${LAINTOOLS_SS_PATH}" \
"${LAINTOOLS_SS_DIR}/${save_date}"
elif has easyocr; then
easyocr \
--lang $(printf %s "${SCREENSHOT_OCR_LANGS}" |sed -e 's/[a-z]+/ /g' -e 's/[a-z]$//g') \
--detail 0 --file "${SCREENSHOT_PATH}" > "${SCREENSHOT_DIR}/${save_date}.txt"
--lang $(printf %s "${LAINTOOLS_SS_OCR_LANGS}" |sed -e 's/[a-z]+/ /g' -e 's/[a-z]$//g') \
--detail 0 --file "${LAINTOOLS_SS_PATH}" > "${LAINTOOLS_SS_DIR}/${save_date}.txt"
fi
}


main() {
check_user_input "$@"
mkdir -p "${SCREENSHOT_DIR}"
mkdir -p "${LAINTOOLS_SS_DIR}"
make_screenshot "$@"
printf '%s\n' "${SCREENSHOT_PATH}"
printf '%s\n' "${LAINTOOLS_SS_PATH}"
copy_to_clipboard
(
# Execute under lock
Expand All @@ -286,4 +332,5 @@ main() {
) 9>"/var/lock/screenshot-$(id -u).lock"
}

main "$@"
# main "$@"
drbttd

0 comments on commit 788f71f

Please sign in to comment.