diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f5503a2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright © 2016, Björn Busse and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Björn Busse nor the + names of contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY Björn Busse ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL Björn Busse BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/not_really_into_pokemon.png b/not_really_into_pokemon.png new file mode 100644 index 0000000..95a71f0 Binary files /dev/null and b/not_really_into_pokemon.png differ diff --git a/xkcdlock b/xkcdlock new file mode 100755 index 0000000..3af0d44 --- /dev/null +++ b/xkcdlock @@ -0,0 +1,285 @@ +#!/usr/bin/env rbash + +# xkcd_lock v1 +# +# A wrapper around i3lock to display xkcd images on the lock screen +# +# 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, xrandr, awk, curl +# +# © 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 +# - add tooltip overlay and xkcd number +# - parallelize downloads + +[[ "$TRACE" ]] && set -x +set -eo pipefail + +readonly SCRIPT_NAME=$(basename $0) + +# screensaver executable +LOCK_BIN="i3lock" +# verbosity +VERBOSE=0 +# "latest" or "random" +IMG_CHOICE="random" +# path to images +IMG_PATH="${HOME}/Pictures/xkcd_comics" +# default / fallback image +IMG_DEFAULT="not_really_into_pokemon.png" +# background colour +BG_COLOUR="white" + +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" +} + +xkcd_get_latest_image() { + if [[ -z $(which curl) ]]; then + error "Could not find curl to get latest image" + exit 1 + fi + + log "Looking for latest image" + local img_url=$(curl -s http://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() { + echo $(echo "$img_url_hotlink" | awk 'BEGIN{FS="/";} {print $5}') +} + +xkcd_get_all_images() { + VERBOSE="1" + + if [[ -z $(which curl) ]]; then + error "Could not find curl to download images" + exit 1 + fi + + log "Looking for latest image" + local nimg_latest=$(curl -s http://xkcd.com/index.html | \ + awk '/Permanent link to this comic: / {print $6}' | \ + awk 'BEGIN{FS="/";} {print $4}') + + log "Found: $nimg_latest" + + 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) + + if [[ -e "$img_name" ]]; then + log "$img_name exists. Skipping download" + continue + fi + + log "Downloading ${i} ${img_url_hotlink} (${img_name})" + local r=$(curl -sO $img_url_hotlink) + log "Success: $r" + done + + echo 0 +} + +xkcd_get_hotlink_url() { + local url="http://xkcd.com/$i" + local url_hotlink="$(curl -sL $url | awk '/Image URL \(for hotlinking\/embedding\): / {print $5}')" + echo $url_hotlink +} + +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 || $1max {line=$0; max=$1}; END {print line}') + + echo "$res" +} + +get_random_image() { + local img_fn="$(find $IMG_PATH -type f | shuf -n 1)" + + if ! [[ -e "$img_fn" ]]; then + error "Could not find image to display" + fi + + echo "$img_fn" +} + +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 tmp_file_r=$(resize_image $img_fn $res) + local tmp_file_c=$(center_image $tmp_file_r $res) + + echo "$tmp_file_c" +} + +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" + fi + ;; + m) + IMG_CHOICE="${OPTARG}" + ;; + v) + VERBOSE=1 + ;; + y) + r=$(xkcd_get_all_images) + exit 0 + esac + done + + if ! [[ -d "$IMG_PATH" ]]; then + error "Image directory does not exist: $IMG_PATH" + exit 1 + fi + + if [[ -z $(which xrandr) ]]; then + error "Could not find xrandr to determine screen size" + exit 1 + fi + + if [[ -z "$locker" ]]; then + error "Could not find screen locker: $lock_bin" + exit 1 + fi + + if [[ -z $(which convert) ]]; then + error "Could not find $convert_bin \nYou need imagick" + 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 "$@"