#!/usr/bin/env sh
# File: keyboard-leds-trayicons.sh
# License: CC-BY-SA 4.0
# Author: bgstack15
# Startdate: 2020-02-13 15:30
# Title: Substitute for Keyboard LED Indicators
# Purpose: For systems that do not show LEDs for capslock and numlock
# History:
# Usage:
# Reference:
#    some Internet place that demonstrated how to use xset q
#    some Internet place that showed how to convert the decimal to binary with awk
#    get_conf comes from framework.sh
# Improve:
#    provide way to change status of capslock by clicking the icon?
# Dependencies:
#    raw: mktrayicon, awk, xset
#    devuan: mktrayicon, mawk | gawk, x11-xserver-utils

# little c: https://visualpharm.com/free-icons/c%20letter-595b40b65ba036ed117d1027
# license for icons: Use for free, but link to [icons8](https://icons8.com/license)

# CONFIG FILES
test -z "${KLT_GLOBAL_CONF}" && KLT_GLOBAL_CONF=/etc/keyboard-leds-trayicons.conf
test -z "${KLT_USER_CONF}" && KLT_USER_CONF="${HOME}/.config/keyboard-leds-trayicons.conf"
# also accept KLT_CONF

# FUNCTIONS

get_conf() {
   # Ripped from framework.sh
   # call: get_conf "${conffile}"
   local _infile="$1"
   local _tmpfile1="$( mktemp )"
   sed -e 's/^\s*//;s/\s*$//;/^[#$]/d;s/\s*[^\]#.*$//;' "${_infile}" | grep -viE "^$" | while read _line ;
   do
      local _left="$( echo "${_line}" | cut -d'=' -f1 )"
      eval "_thisval=\"\${${_left}}\""
      test -z "${_thisval}" && echo "${_line}" >> "${_tmpfile1}"
   done
   test -f "${_tmpfile1}" && { . "${_tmpfile1}" 1>/dev/null 2>&1 ; }
   /bin/rm -rf "${_tmpfile1}" 1>/dev/null 2>&1
}

clean_keyboard_leds_trayicons() {
   { test -e "${capslockicon}" && echo "q" > "${capslockicon}" ; } 1>/dev/null 2>&1 &
   { test -e "${numlockicon}" && echo "q" > "${numlockicon}" ; } 1>/dev/null 2>&1 &
   sleep 1 && rm -f "${capslockicon}" "${numlockicon}" "${KLT_KILLFILE}"
}

get_led_mask() {
   xset q | awk 'function d2b(d,b) {while(d) {b=d%2b;d=int(d/2)}return(b)} /LED/{print d2b($NF)}'
}

mask_position_on() {
   # call: mask_position_on "${LEDS}" "${ledposition}"
   ___result="$( printf "%08d" "${1}" | tail -c "${2}" | head -c 1 )"
   ___result=$(( 1 - ___result ))
   return "${___result}"
}

is_capslock_on() {
   # call: get_led_mask | is_capslock_on && echo "yes"
   read ___ico
   mask_position_on "${___ico}" "1"
}

is_numlock_on() {
   # call: get_led_mask | is_numlock_on && echo "yes"
   read ___ico
   mask_position_on "${___ico}" "2"
}

# LOAD CONFIGS
# order is important! The last one called gets precedence.
# instead of simply dot-sourcing the conf file, pass it to get_conf which only applies new values, so this process's environment is preserved
for thisconf in "${KLT_CONF}" "${KLT_USER_CONF}" "${KLT_GLOBAL_CONF}" ;
do
   test -r "${thisconf}" && get_conf "${thisconf}"
done

# DEFAULTS in case configs did not have these values
test -z "${KLT_CAPS_ON_ICON}" && KLT_CAPS_ON_ICON=capslock-on
test -z "${KLT_CAPS_OFF_ICON}" && KLT_CAPS_OFF_ICON=capslock-off
test -z "${KLT_CAPS_ON_ICON}" && KLT_CAPS_ON_ICON=numlock-on
test -z "${KLT_CAPS_OFF_ICON}" && KLT_CAPS_OFF_ICON=numlock-off
test -z "${KLT_KILLFILE}" && KLT_KILLFILE="/tmp/kill-all-leds-trayicons"

# INITIALIZATION

capslockicon="/var/run/user/$( id -u )/${$}.cap.icon"
numlockicon="/var/run/user/$( id -u )/${$}.num.icon"

test "ON" = "ON" && {
   mkfifo "${capslockicon}"
   mkfifo "${numlockicon}"

   mktrayicon "${capslockicon}" &
   mktrayicon "${numlockicon}" &

   echo "m quit,echo 'q' > ${capslockicon} ; touch "${KLT_KILLFILE}"" > "${capslockicon}"
   echo "m quit,echo 'q' > ${numlockicon} ; touch "${KLT_KILLFILE}"" > "${numlockicon}"
}

caps_status_old=-1
num_status_old=-1

rm -f "${KLT_KILLFILE}"

trap 'trap "" 2 ; touch "${KLT_KILLFILE}" '  2 # CTRL-C

while ! test -e "${KLT_KILLFILE}" 2>/dev/null ;
do
   x="$( get_led_mask )"
   caps_status_now=0
   num_status_now=0
   echo "${x}" | is_capslock_on && caps_status_now=1
   echo "${x}" | is_numlock_on && num_status_now=1

   if test "${caps_status_now}" != "${caps_status_old}" ;
   then
      test -p "${capslockicon}" && case "${caps_status_now}" in
         1)
            test -n "${KLT_DEBUG}" && echo "capslock on (icon file ${KLT_CAPS_ON_ICON})" 1>&2
            echo "i ${KLT_CAPS_ON_ICON}" > "${capslockicon}"
            echo "t capslock on" > "${capslockicon}"
            ;;
         0)
            test -n "${KLT_DEBUG}" && echo "capslock off (icon file ${KLT_CAPS_OFF_ICON})" 1>&2
            echo "i ${KLT_CAPS_OFF_ICON}" > "${capslockicon}" 
            echo "t capslock off" > "${capslockicon}"
            ;;
      esac
   fi

   if test "${num_status_now}" != "${num_status_old}" ;
   then
      test -p "${numlockicon}" && case "${num_status_now}" in
         1)
            test -n "${KLT_DEBUG}" && echo "numlock on (icon file ${KLT_NUM_ON_ICON})" 1>&2
            echo "i ${KLT_NUM_ON_ICON}" > "${numlockicon}"
            echo "t numlock on" > "${numlockicon}"
            ;;
         0) 
            test -n "${KLT_DEBUG}" && echo "numlock off (icon file ${KLT_NUM_OFF_ICON})" 1>&2
            echo "i ${KLT_NUM_OFF_ICON}" > "${numlockicon}"
            echo "t numlock off" > "${numlockicon}"
            ;;
      esac
   fi
      
   caps_status_old="${caps_status_now}"
   num_status_old="${num_status_now}"
   sleep 0.75
done

# safety shutoff
clean_keyboard_leds_trayicons
