245 lines
6.2 KiB
Plaintext
Executable File
245 lines
6.2 KiB
Plaintext
Executable File
#!/usr/bin/env nix-shell
|
|
#!nix-shell -i bash -p coreutils -p killall -p playerctl -p procps -p sane-open-desktop -p sway -p util-linux -p wireplumber
|
|
|
|
# input map considerations
|
|
# - using compound actions causes delays.
|
|
# e.g. if volup->volup is a distinct action from volup, then single-volup action is forced to wait the maximum button delay.
|
|
# - actions which are to be responsive should therefore have a dedicated key.
|
|
# - a dedicated "kill" combo is important for unresponsive fullscreen apps, because appmenu doesn't show in those
|
|
# - although better may be to force appmenu to show over FS apps
|
|
# - bonsai mappings are static, so buttons can't benefit from non-compounding unless they're mapped accordingly for all lock states
|
|
# - this limitation could be removed, but with work
|
|
#
|
|
# example of a design which considers these things:
|
|
# - when unlocked:
|
|
# - volup tap -> app menu
|
|
# - volup hold -> file browser
|
|
# - voldown press -> keyboard
|
|
# - voldown hold -> terminal
|
|
# - power x2 -> screenoff
|
|
# - power tap->hold -> kill app
|
|
# - power,volup -> screen rotate CCW
|
|
# - power,voldown -> screen rotate CW
|
|
# - power+volup -> screenshot
|
|
# - power+voldown -> camera
|
|
# - when locked:
|
|
# - volup tap -> volume up
|
|
# - volup hold -> media seek forward
|
|
# - voldown tap -> volume down
|
|
# - voldown hold -> media seek backward
|
|
# - power tap -> screen on
|
|
# - power hold -> play/pause media
|
|
# limitations/downsides:
|
|
# - voldown hold is over eager: easy to open terminals when phone is slow.
|
|
# - remap to voldown tap->hold ?
|
|
#
|
|
# EXAMPLE EVENT FIRINGS:
|
|
# - double-tap voldown:
|
|
# - voldown_start
|
|
# - voldown_tap_1
|
|
# - voldown_tap_2
|
|
# - hold voldown:
|
|
# - voldown_start
|
|
# - voldown_hold_1
|
|
# - voldown_hold_2
|
|
# - voldown_hold_3
|
|
# - hold power:
|
|
# - power_hold (notice: it doesn't fire power_start)
|
|
# - double-tap power:
|
|
# - power_tap_1
|
|
# - power_tap_2
|
|
# - power tap-then-hold:
|
|
# - power_tap_1
|
|
# - power_tap_1_hold
|
|
|
|
|
|
|
|
# increments to use for volume adjustment (in %)
|
|
VOL_INCR=5
|
|
KEYBOARD="${KEYBOARD:-wvkbd-mobintl}"
|
|
|
|
action="$1"
|
|
|
|
isTouchOn() {
|
|
# success if all touch inputs have their events enabled
|
|
swaymsg -t get_inputs --raw \
|
|
| jq --exit-status '. | map(select(.type == "touch")) | all(.libinput.send_events == "enabled")' \
|
|
> /dev/null
|
|
}
|
|
isScreenOn() {
|
|
# success if all outputs have power
|
|
swaymsg -t get_outputs --raw \
|
|
| jq --exit-status '. | all(.power)' \
|
|
> /dev/null
|
|
}
|
|
|
|
isAllOn() {
|
|
isTouchOn && isScreenOn
|
|
}
|
|
|
|
isInhibited() {
|
|
pidof rofi
|
|
}
|
|
|
|
|
|
ignore() {
|
|
true
|
|
}
|
|
inhibited() {
|
|
true
|
|
}
|
|
unmapped() {
|
|
true
|
|
}
|
|
|
|
allOn() {
|
|
swaymsg -- output '*' power true
|
|
swaymsg -- input type:touch events enabled
|
|
}
|
|
allOff() {
|
|
swaymsg -- output '*' power false
|
|
swaymsg -- input type:touch events disabled
|
|
}
|
|
|
|
toggleKeyboard() {
|
|
local kbpid=$(pidof "$KEYBOARD")
|
|
if [ -z "$kbpid" ] || ! ( env kill -s RTMIN+0 "$kbpid" ); then
|
|
echo "sane-input-handler: failed to toggle keyboard: $KEYBOARD"
|
|
fi
|
|
}
|
|
|
|
handleWith() {
|
|
state=
|
|
if [ -n "$_isInhibited" ]; then
|
|
state="inhibited+"
|
|
fi
|
|
if [ -n "$_isAllOn" ]; then
|
|
state="${state}on"
|
|
else
|
|
state="${state}off"
|
|
fi
|
|
echo "sane-input-handler: state=$state action=$action: handleWith: $@"
|
|
"$@"
|
|
exit 0
|
|
}
|
|
|
|
dispatchDefault() {
|
|
case "$action" in
|
|
"power_tap_2")
|
|
# power twice => screenoff
|
|
handleWith allOff
|
|
;;
|
|
"power_hold")
|
|
# power twice => toggle media player
|
|
handleWith playerctl play-pause
|
|
;;
|
|
|
|
volup_tap*)
|
|
handleWith wpctl set-volume @DEFAULT_AUDIO_SINK@ "$VOL_INCR"%+
|
|
;;
|
|
voldown_tap*)
|
|
handleWith wpctl set-volume @DEFAULT_AUDIO_SINK@ "$VOL_INCR"%-
|
|
;;
|
|
esac
|
|
}
|
|
|
|
dispatchOff() {
|
|
case "$action" in
|
|
"power_tap_1")
|
|
# power once => unlock
|
|
handleWith allOn
|
|
;;
|
|
volup_hold*)
|
|
handleWith playerctl position 30+
|
|
;;
|
|
voldown_hold*)
|
|
handleWith playerctl position 10-
|
|
;;
|
|
esac
|
|
}
|
|
|
|
dispatchOn() {
|
|
case "$action" in
|
|
# power_tap_1: intentional default to no-op (it's important this be unmapped, because events can be misordered with power_tap_1 arriving *after* power_tap_2)
|
|
# power_tap_2: intentional default to screenoff
|
|
"power_tap_1_hold")
|
|
# power tap->hold: kill active window
|
|
# TODO: disable this if locked (with e.g. schlock, swaylock, etc)
|
|
handleWith swaymsg kill
|
|
;;
|
|
"power_and_volup")
|
|
# power (hold) -> volup: take screenshot
|
|
handleWith sane-open-desktop sane-screenshot.desktop
|
|
;;
|
|
"power_and_voldown")
|
|
# power (hold) -> voldown: open camera
|
|
handleWith sane-open-desktop org.postmarketos.Megapixels.desktop
|
|
;;
|
|
"power_then_volup")
|
|
# power (tap) -> volup: rotate CCW
|
|
handleWith swaymsg -- output '-' transform 90 anticlockwise
|
|
;;
|
|
"power_then_voldown")
|
|
# power (tap) -> voldown: rotate CW
|
|
handleWith swaymsg -- output '-' transform 90 clockwise
|
|
;;
|
|
|
|
"volup_tap_1")
|
|
# volume up once: system menu
|
|
handleWith sane-open-desktop rofi.desktop
|
|
;;
|
|
"volup_hold_1")
|
|
# volume up hold: just browse files
|
|
handleWith sane-open-desktop rofi-filebrowser.desktop
|
|
;;
|
|
|
|
"voldown_start")
|
|
# volume down once: toggle keyboard
|
|
handleWith toggleKeyboard
|
|
;;
|
|
"voldown_hold_1")
|
|
# hold voldown to launch terminal
|
|
# note we already triggered the keyboard; that's fine: usually keyboard + terminal go together :)
|
|
handleWith sane-open-desktop xdg-terminal-exec.desktop
|
|
;;
|
|
"voldown_tap_1")
|
|
# swallow, to prevent keyboard from also triggering media controls
|
|
handleWith ignore
|
|
;;
|
|
voldown_hold_*)
|
|
# swallow, to prevent terminal from also triggering media controls
|
|
handleWith ignore
|
|
;;
|
|
esac
|
|
}
|
|
|
|
dispatchInhibited() {
|
|
case "$action" in
|
|
"power_tap_1_hold")
|
|
# power hold: escape hatch in case rofi has hung
|
|
handleWith killall -9 rofi
|
|
;;
|
|
*)
|
|
# eat everything else (and let rofi consume it)
|
|
handleWith inhibited
|
|
;;
|
|
esac
|
|
}
|
|
|
|
_isAllOn="$(isAllOn && echo 1 || true)"
|
|
|
|
if [ -z "$_isAllOn" ]; then
|
|
dispatchOff
|
|
else
|
|
_isInhibited="$(isInhibited && echo 1 || true)"
|
|
if [ -n "$_isInhibited" ]; then
|
|
dispatchInhibited
|
|
else
|
|
dispatchOn
|
|
fi
|
|
fi
|
|
|
|
dispatchDefault
|
|
|
|
handleWith unmapped
|