#!/bin/bash # i3hwlock # # Allows locking i3 by the presence of a file. This file is intended to be # created using udev rules so that you can trigger the lock when you remove # a device such as a YubiKey. Example rule below. # # /etc/udev/rules.d/99-i3hwlock-yubikey.rules: # ACTION=="add", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="1050", RUN+="/usr/bin/rm /tmp/i3hwlock.trigger" # ACTION=="remove", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="1050", RUN+="/usr/bin/touch /tmp/i3hwlock.trigger" LOCK_TIMEOUT=3 # How many seconds to wait before locking. DMENU_FILE="/tmp/i3hwlock" TRIGGER_FILE="$DMENU_FILE.trigger" ABORT_FILE="$DMENU_FILE.abort" CONTINUE_FILE="$DMENU_FILE.continue" LOCKING_FILE="$DMENU_FILE.locking" function check_prerequisites() { [ -e "$ABORT_FILE" ] && rm "$ABORT_FILE" if ! which inotifywait >/dev/null; then echo "ERROR: inotifywait not found." 1>&2 return 1 fi } function dmenu_selection() { selection="$(xargs -n 1 <<<"Continue Abort" | dmenu -i -p "The screen is about to lock")" [ -z "$selection" ] && return file="$DMENU_FILE.${selection,,}" echo "INFO: Touching $file" touch "$file" } function abort_lock() { [ -e "$ABORT_FILE" ] && rm "$ABORT_FILE" if ! [ -f "$LOCKING_FILE" ]; then echo "WARN: Abort initiated without file '$LOCKING_FILE'." 1>&2 return 0 fi echo "INFO: Aborting lock." killall dmenu notify-send "Lock aborted." & [ -e $LOCKING_FILE ] && rm "$LOCKING_FILE" } function lock() { [ -e "$CONTINUE_FILE" ] && rm "$CONTINUE_FILE" if ! [ -f "$LOCKING_FILE" ]; then echo "WARN: Won't lock as file '$LOCKING_FILE' is missing." 1>&2 return 0 fi echo "INFO: Locking now." killall dmenu i3exit lock 2>/dev/null || i3lock --color=29414f --nofork [ -e $LOCKING_FILE ] && rm "$LOCKING_FILE" } function delayed_lock() { echo "INFO: Delayed lock initiated." touch "$DMENU_FILE.locking" for i in $(seq $LOCK_TIMEOUT); do notify-send "Locking in $((LOCK_TIMEOUT - i + 1))" -t 1000 & sleep 1 if [ -e "$ABORT_FILE" ]; then abort_lock return 0 elif [ -e "$CONTINUE_FILE" ]; then break fi done ! [ -e $LOCKING_FILE ] && return 0 lock } function listen() { while read -r directory action file; do if [[ "$file" == "${TRIGGER_FILE##*/}" ]]; then if [[ "$action" == "CLOSE_WRITE,CLOSE" ]] && ! [ -e "$LOCKING_FILE" ]; then $0 dmenu-selection & $0 delayed-lock & elif [[ "$action" == "DELETE" ]]; then abort_lock fi fi done < <(inotifywait -m "${TRIGGER_FILE%/*}") } check_prerequisites || exit 1 if [ -z "$1" ]; then listen || exit 2 elif [[ "$1" == delayed-lock ]]; then delayed_lock || exit 3 elif [[ "$1" == dmenu-selection ]]; then dmenu_selection || exit 4 else echo "ERROR: Unrecognized action: '$1'." 1>&2 exit 127 fi