Skip to content

Instantly share code, notes, and snippets.

@salewski
Last active September 22, 2020 18:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save salewski/abc0c9dfc6657fec4bcadc55fc5c0017 to your computer and use it in GitHub Desktop.
Save salewski/abc0c9dfc6657fec4bcadc55fc5c0017 to your computer and use it in GitHub Desktop.
invoked by inputplug(1) to respond to device add/remove events
#!/bin/bash -
# SPDX-FileCopyrightText: <text> © 2020 Alan D. Salewski <ads@salewski.email> </text>
#
# SPDX-License-Identifier: GPL-2.0-or-later
# on-new-kbd: Applies a canned X11 keyboard config, when invoked.
#
# Intended to be invoked by the inputplug(1) daemon, in response to
# devices being added/removed, enabled/disabled, etc.
#
# As featured in the article:
#
# "Left-hand ENTER key"
# by Alan D. Salewski
# 2020-09-22
# https://salewski.github.io/2020/09/22/left-hand-enter-key.html
#
#
# Usage:
# ------
#
# For interactive testing, do this:
#
# $ inputplug -d -c /path/to/on-new-kbd
#
# As part of your X11 startup process, add the following to your
# ~/.xsession (note: no '-d' opt -- will run as daemon, not in
# foreground):
#
# inputplug -c /path/to/on-new-kbd
#
# Output will be written to the location in hard-coded in $MY_LOG_FPATH
# below.
#
# Motivation:
# -----------
# In X11, keyboard settings get applied only to the devices attached to
# the sytem when the settings are applied. If, for example, a USB
# keyboard is unplugged and then re-plugged, it will get default
# settings, not the override settings that we would have applied during
# X11 initialization.
#
# Because 'inputplug' monitors the system for XInput events, it can
# determine when a new device has been (re)added to the system. It
# invokes this program with the event metadata, which gives a chance to
# (re)apply our override settings.
#
# Attribution:
# ------------
# Adapted from the approach suggested by unix.stackexchange.com user
# 'mosvy'[0] in the comment:
#
# https://unix.stackexchange.com/a/523959
#
# Top-level of that thread:
#
# "Prevent keyboard layout reset when USB keyboard is plugged in"
# https://unix.stackexchange.com/questions/523635/prevent-keyboard-layout-reset-when-usb-keyboard-is-plugged-in
#
# [0] https://unix.stackexchange.com/users/308316/mosvy
#
# On Debian-based systems, the 'inputplug' program is supplied by the
# 'inputplug' Debian package.
declare -r PROG='on-new-kbd'
declare -r MY_LOG_DIR="${HOME}/var/log/${PROG}"
declare -r MY_LOG_FPATH="${MY_LOG_DIR}/${PROG}.log"
declare -r PROG_FOR_EVT_SLAVE_KEYBOARD_ENABLED="${HOME}/bin/ads-keyboard-setup"
if test -e "${MY_LOG_DIR}"; then :; else
mkdir -p "${MY_LOG_DIR}"
if test $? -ne 0; then
printf "${PROG} (error): was unable to create log directory: %s; bailing out\n" \
"${MY_LOG_DIR}" 1>&2
exit 1
fi
fi
dt_now=$(date --rfc-3339 seconds)
if test $? -ne 0; then
printf "${PROG} (error): was unable to obtain the current date and time; bailing out\n" 1>&2
exit 1
fi
printf "${dt_now} ${PROG} (info): program started\n" | tee -a "${MY_LOG_FPATH}"
if test -x "${PROG_FOR_EVT_SLAVE_KEYBOARD_ENABLED}"; then :; else
printf "${dt_now} ${PROG} (error): no such program %s; bailing out" \
"${PROG_FOR_EVT_SLAVE_KEYBOARD_ENABLED}" \
| tee -a "${MY_LOG_FPATH}" 1>&2
exit 1
fi
# When invoked by 'inputplug', four command line parameters will be
# provided:
#
# event-type device-id device-type device-name
#
# See inputplug(1) for details.
if test $# -lt 4; then
printf "${dt_now} ${PROG} (error): expected 4 params, but got only %d; bailing out" "$#" \
| tee -a "${MY_LOG_FPATH}" 1>&2
exit 1
fi
event_type=$1
device_id=$2
device_type=$3
device_name=$4
# No sanity checking of above parameters, since we do not know exactly
# what to expect. We do know that sometimes some of the values will
# legitimately be unset or null. We emit a report of what we received,
# and only respond to a tiny subset of the input that we recognize as an
# actionable for us.
# Examples:
# =========
#
# Note that we get two different events when we unplug the keyboard, and
# two different events when we plug the keyboard back in.
#
# Unplug events:
#
# -----------------------------------------------
# event_type: XIDeviceDisabled XIDeviceDisabled
# device_id: 10 10
# device_type: XISlaveKeyboard XISlaveKeyboard
# device_name: is_unset_or_null
# -----------------------------------------------
# event_type: XISlaveRemoved XISlaveRemoved
# device_id: 10 10
# device_type: is_unset_or_null
# device_name: is_unset_or_null
# -----------------------------------------------
#
#
# Plug event:
#
# -----------------------------------------------
# event_type: XISlaveAdded XISlaveAdded
# device_id: 10 10
# device_type: XIFloatingSlave XIFloatingSlave
# device_name: Microsoft Natural Keyboard Elite Microsoft Natural Keyboard Elite
# -----------------------------------------------
# event_type: XIDeviceEnabled XIDeviceEnabled
# device_id: 10 10
# device_type: XISlaveKeyboard XISlaveKeyboard
# device_name: Microsoft Natural Keyboard Elite Microsoft Natural Keyboard Elite
# -----------------------------------------------
cat - <<EOF | tee -a "${MY_LOG_FPATH}" 1>&2
${dt_now} -----------------------------------------------
${dt_now} event_type: ${event_type:-is_unset_or_null} ${event_type-is_unset}
${dt_now} device_id: ${device_id:-is_unset_or_null} ${device_id-is_unset}
${dt_now} device_type: ${device_type:-is_unset_or_null} ${device_type-is_unset}
${dt_now} device_name: ${device_name:-is_unset_or_null} ${device_name-is_unset}
${dt_now} -----------------------------------------------
EOF
case "${event_type} ${device_type}" in
'XIDeviceEnabled XISlaveKeyboard')
printf "${dt_now} ${PROG} (info): slave keyboard enabled; running config script\n" \
| tee -a "${MY_LOG_FPATH}" 1>&2
"${PROG_FOR_EVT_SLAVE_KEYBOARD_ENABLED}" 2>&1 | tee -a "${MY_LOG_FPATH}" 1>&2
if test $? -ne 0; then
printf "${dt_now} ${PROG} (error): ads-keyboard-setup failed; bailing out" \
| tee -a "${MY_LOG_FPATH}" 1>&2
exit 1
fi
;;
*)
printf "${dt_now} ${PROG} (info): uninteresting event; ignoring (okay)\n" \
| tee -a "${MY_LOG_FPATH}" 1>&2
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment