364 lines
9.6 KiB
Bash
Executable File
364 lines
9.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# xkcdlock v1
|
|
#
|
|
# A wrapper around screen lockers to display xkcd images on the lock screen
|
|
#
|
|
# Depending on the given arguments xkcdlock shows a random image, the latest
|
|
# image or an image of choice.
|
|
#
|
|
# Note that 'latest' needs an internet connection for the download!
|
|
# If the download fails it falls back to a specific hard-coded image
|
|
#
|
|
# Builtin is a function to download all images.
|
|
#
|
|
# Dependencies: rbash, i3lock/swaylock, xrandr, awk, curl, recode,
|
|
# convert (from imagick)
|
|
#
|
|
# © 2016 Björn Busse (see also: LICENSE)
|
|
# bbusse@baerlin.eu
|
|
#
|
|
# Thanks to Randall Patrick Munroe! BTC 1FhCLQK2ZXtCUQDtG98p6fVH7S6mxAsEey
|
|
# Thanks to Michael Stapelberg for an awesome window manager - i3
|
|
#
|
|
# TODO:
|
|
# - Make sure we do not exceed screen boundaries in x and y
|
|
# - Improve tooltip text border overflow prevention
|
|
# wrt display resolution (and font size?)
|
|
# - Add support for other screen lockers
|
|
# - Parallelize downloads
|
|
|
|
[[ "$TRACE" ]] && set -x
|
|
set -eo pipefail
|
|
|
|
readonly SCRIPT_NAME=$(basename $0)
|
|
|
|
# screensaver executable
|
|
LOCK_BIN="swaylock"
|
|
# verbosity
|
|
VERBOSE=0
|
|
# "latest" or "random"
|
|
IMG_CHOICE="random"
|
|
# path to images
|
|
IMG_PATH="${HOME}/Pictures/xkcd"
|
|
# default / fallback image
|
|
IMG_DEFAULT="not_really_into_pokemon.png"
|
|
# background colour
|
|
BG_COLOUR="white"
|
|
# Break tooltip every x char to prevent border overflow
|
|
TOOLTIP_MAX_LINE_LEN=100
|
|
|
|
FONT="xkcd Script"
|
|
FONT_FILE="xkcd-script.ttf"
|
|
FONT_PATH="${HOME}/.local/share/fonts/${FONT_FILE}"
|
|
URL_FONT="https://github.com/ipython/xkcd-font/raw/master/xkcd-script/font/xkcd-script.ttf"
|
|
|
|
declare -a DEPS=("xrandr" "awk" "curl" "convert" "recode" "sed")
|
|
declare -a DEPS_LOCKER=("i3lock" "swaylock")
|
|
|
|
DOWNLOAD_DISCLAIMER="\nThe downloaded images will end up in your current working directory.\n\
|
|
Since we are using restricted bash, we can not change path.\n\
|
|
Use '-y' instead of '-d' to really start the download to the current working directory.\n"
|
|
|
|
log() {
|
|
if (( 1=="${VERBOSE}" )); then
|
|
echo "$@" >&2
|
|
fi
|
|
|
|
logger -p user.notice -t $SCRIPT_NAME "$@"
|
|
}
|
|
|
|
error() {
|
|
echo "$@" >&2
|
|
logger -p user.error -t $SCRIPT_NAME "$@"
|
|
}
|
|
|
|
# does not work with restricted bash (bash -r/rbash)
|
|
get_script_path() {
|
|
echo $(dirname $(readlink -f $0))
|
|
}
|
|
|
|
show_help() {
|
|
printf "\n xkcd_lock v1\n\n\
|
|
Available options are:\n\n \
|
|
-d download images to current working directory\n \
|
|
-h show this help\n \
|
|
-i show specific image file - overrides '-m'\n \
|
|
-l lock program: one of i3lock/swaylock\n \
|
|
-m latest|random default: random\n \
|
|
-v be verbose\n\n"
|
|
}
|
|
|
|
check_dependencies() {
|
|
for i in "${DEPS[@]}"
|
|
do
|
|
if [[ -z $(which "${i}") ]]; then
|
|
error "Could not find ${i}"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
if fc-list -q "${FONT}"; then
|
|
log "Font exists"
|
|
else
|
|
log "Downloading font"
|
|
curl -o "${HOME}/${FONT_PATH}" -sLO "${URL_FONT}"
|
|
fi
|
|
}
|
|
|
|
xkcd_get_latest_image() {
|
|
log "Looking for latest image"
|
|
local img_url=$(curl -s https://xkcd.com/index.html | \
|
|
awk '/Image URL \(for hotlinking\/embedding\): / {print $5}' | \
|
|
awk 'BEGIN{FS="<";} {print $1}')
|
|
|
|
if [[ -z $img_url ]]; then
|
|
error "Can not download latest image, using fallback image"
|
|
local img_fn="${IMG_DEFAULT}"
|
|
else
|
|
log "Downloading: $img_url"
|
|
curl -sO --max-time 6 "$img_url"
|
|
|
|
if [[ 0="$?" ]]; then
|
|
local img_fn=$(echo "$img_url" | awk 'BEGIN{FS="/";} {print $5}')
|
|
else
|
|
error "Can not download latest image, using fallback image"
|
|
local img_fn="${IMG_DEFAULT}"
|
|
fi
|
|
fi
|
|
|
|
echo "$img_fn"
|
|
}
|
|
|
|
xkcd_get_img_name() {
|
|
local fn=$(echo "$img_url_hotlink" | awk 'BEGIN{FS="/";} {print $5}')
|
|
IFS='.' read -r -a array <<< "${fn}"
|
|
fn="${array[0]}"_"${i}"."${array[-1]}"
|
|
echo "$fn"
|
|
}
|
|
|
|
xkcd_get_img_name_from_file() {
|
|
IFS='/' read -r -a array <<< "$img_fn"
|
|
IFS='.' read -r -a array <<< "${array[-1]}"
|
|
IFS='_' read -r -a array <<< "${array[0]}"
|
|
local fn=${array[-1]}
|
|
echo "$fn"
|
|
}
|
|
|
|
xkcd_get_img_nr() {
|
|
IFS='.' read -r -a array <<< "$img_name"
|
|
IFS='_' read -r -a array <<< "${array[0]}"
|
|
echo "${array[-1]}"
|
|
}
|
|
|
|
xkcd_get_all_images() {
|
|
VERBOSE="1"
|
|
log "Looking for latest image"
|
|
local nimg_latest=$(curl -s https://xkcd.com/index.html | \
|
|
awk '/Permanent link to this comic: / {print $6}' | \
|
|
awk 'BEGIN{FS="/";} {print $4}')
|
|
|
|
if [[ -z "$nimg_latest" ]]; then
|
|
VERBOSE=1
|
|
error "Failed to find latest image number, not downloading anything"
|
|
exit 1
|
|
else
|
|
log "Found: $nimg_latest"
|
|
fi
|
|
|
|
for ((i=1; i<=$nimg_latest; i++)); do
|
|
local img_url_hotlink=$(xkcd_get_hotlink_url $i)
|
|
local img_name=$(xkcd_get_img_name $img_url_hotlink $i)
|
|
|
|
if [[ -e "$img_name" ]]; then
|
|
log "$img_name exists. Skipping download"
|
|
continue
|
|
fi
|
|
|
|
log "Downloading #${i} ${img_url_hotlink} (${img_name})"
|
|
$(curl -s $img_url_hotlink -o $img_name)
|
|
if (( 0 != "$?" )); then
|
|
error "Failed to download ${i}"
|
|
fi
|
|
|
|
if [ "${img_name: -3}" == "jpg" ]; then
|
|
convert_image $img_name
|
|
fi
|
|
done
|
|
|
|
echo 0
|
|
}
|
|
|
|
xkcd_get_hotlink_url() {
|
|
local url="https://xkcd.com/$i"
|
|
local url_hotlink="$(curl -sL $url | awk '/Image URL \(for hotlinking\/embedding\): / {print $5}')"
|
|
echo $url_hotlink
|
|
}
|
|
|
|
xkcd_get_img_tooltip() {
|
|
local url="https://xkcd.com/$1"
|
|
echo $(curl -sL $url | grep -A 1 '<div id=\"comic\">' | awk -F "\"" '/src=/ {print $4}' | recode html..UTF8)
|
|
}
|
|
|
|
xkcd_format_tooltip() {
|
|
local text=$(echo $text | fold -s -w $TOOLTIP_MAX_LINE_LEN)
|
|
echo "$text"
|
|
}
|
|
|
|
get_nscreens() {
|
|
local nscreens=$(xrandr -q | awk '/ connected/ {count++} END {print count}')
|
|
echo "$nscreens"
|
|
}
|
|
|
|
screen_get_smallest_resolution() {
|
|
local res=$(xrandr -q | awk '/*/ {print $1}' \
|
|
| awk 'BEGIN{FS="x";} NR==1 || $1<min {line=$0; min=$1}; END {print line}')
|
|
|
|
echo "$res"
|
|
}
|
|
|
|
screen_get_highest_resolution() {
|
|
local res=$(xrandr -q | awk '/\*/ {print $1}' \
|
|
| awk 'BEGIN{FS="x";} NR==1 || $1>max {line=$0; max=$1}; END {print line}')
|
|
|
|
echo "$res"
|
|
}
|
|
|
|
get_random_image() {
|
|
local img_fn="$(find $IMG_PATH -type f | sort -R | head -n1 )"
|
|
|
|
if ! [[ -e "$img_fn" ]]; then
|
|
error "Could not find image to display"
|
|
fi
|
|
|
|
echo "$img_fn"
|
|
}
|
|
|
|
convert_image() {
|
|
local img_name_len=$((${#img_name} - 4))
|
|
local img_name_png=${img_name:0:$img_name_len}".png"
|
|
log "Converting $img_name to $img_name_png"
|
|
$(convert $img_name $img_name_png)
|
|
$(rm -f $img_name)
|
|
}
|
|
|
|
image_add_text() {
|
|
local tmp_file=$(mktemp)
|
|
|
|
log "Adding \""${2}"\" to ${1} and saving image to ${tmp_file}"
|
|
$(convert "${1}" -font "${5}" -gravity "${3}" -pointsize "${7}" -fill "${4}" $8 -annotate "${6}" "${2}" "${tmp_file}")
|
|
|
|
if ! [[ -e "$tmp_file" ]]; then
|
|
error "Could not find image with text overlay"
|
|
exit 1
|
|
fi
|
|
|
|
echo "$tmp_file"
|
|
}
|
|
|
|
resize_image() {
|
|
local tmp_file=$(mktemp)
|
|
|
|
log "Resizing $img_fn to $res and saving image to $tmp_file"
|
|
convert -adaptive-resize $res $img_fn $tmp_file
|
|
|
|
if ! [[ -e "$tmp_file" ]]; then
|
|
error "Could not find resized image"
|
|
exit 1
|
|
fi
|
|
|
|
echo "$tmp_file"
|
|
}
|
|
|
|
center_image() {
|
|
local tmp_file=$(mktemp)
|
|
|
|
log "Centering $tmp_file and saving image to $tmp_file"
|
|
$(convert $tmp_file_r -gravity center -background $BG_COLOUR -extent $res $tmp_file)
|
|
|
|
if ! [[ -e "$tmp_file" ]]; then
|
|
error "Could not find centered image"
|
|
exit 1
|
|
fi
|
|
|
|
echo "$tmp_file"
|
|
}
|
|
|
|
prepare_image() {
|
|
local res=$(screen_get_highest_resolution)
|
|
local img_name=$(xkcd_get_img_name_from_file $img_fn)
|
|
local img_nr=$(xkcd_get_img_nr $img_name)
|
|
local text=$(xkcd_get_img_tooltip $img_nr)
|
|
img_tooltip=$(xkcd_format_tooltip $text)
|
|
|
|
local tmp_file_r=$(resize_image $img_fn $res)
|
|
local tmp_file_c=$(center_image $tmp_file_r $res)
|
|
local tmp_file_t1=$(image_add_text "${tmp_file_c}" "${img_nr}" "Northeast" "green" "${FONT_PATH}" "+50+50" "50")
|
|
local tmp_file_t2=$(image_add_text "${tmp_file_t1}" "${img_tooltip}" "Southwest" "red" "${FONT_PATH}" "+0+0" "30" " -undercolor '#00000080' ")
|
|
|
|
echo "$tmp_file_t2"
|
|
}
|
|
|
|
screen_lock() {
|
|
local img_fn_final=$(prepare_image $img_fn)
|
|
|
|
log "Locking screen with $img_fn"
|
|
$LOCK_BIN -i $img_fn_final
|
|
}
|
|
|
|
main() {
|
|
local locker=$(which $LOCK_BIN)
|
|
local OPTIND
|
|
|
|
while getopts "h?i:l:vm:dy" opt; do
|
|
case "$opt" in
|
|
d)
|
|
printf "%s${DOWNLOAD_DISCLAIMER}"
|
|
printf "%s\nCurrent working directory: ${PWD}\n\n"
|
|
exit 0
|
|
;;
|
|
h|\?)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
i)
|
|
local img_fn="${OPTARG}"
|
|
local r=$(screen_lock $img_fn)
|
|
exit 0
|
|
;;
|
|
l)
|
|
if [[ "sway"="${OPTARG}" ]]; then
|
|
LOCK_BIN="swaylock"
|
|
echo "Not yet implemented since we have no xrandr with wayland"
|
|
exit 1
|
|
fi
|
|
;;
|
|
m)
|
|
IMG_CHOICE="${OPTARG}"
|
|
;;
|
|
v)
|
|
VERBOSE=1
|
|
;;
|
|
y)
|
|
r=$(xkcd_get_all_images)
|
|
exit 0
|
|
esac
|
|
done
|
|
|
|
check_dependencies
|
|
|
|
if ! [[ -d "$IMG_PATH" ]]; then
|
|
error "Image directory does not exist: $IMG_PATH"
|
|
exit 1
|
|
fi
|
|
|
|
case "$IMG_CHOICE" in
|
|
"latest") local img_fn=$(xkcd_get_latest_image);;
|
|
"random") local img_fn=$(get_random_image);;
|
|
esac
|
|
|
|
screen_lock $img_fn
|
|
}
|
|
|
|
main "$@"
|