Blob Blame Raw
#!/bin/bash

set -e

# directory to store pids of background processes
PIDS=/dev/shm/cool-rotate

# command to run a screen keyboard
KEYBOARD="coolkbd"
# arguments for the screen keyboard
KEYBOARD_ARGS="/dev/input/event1"

# command to run sensors monitor (which do the screen auto-rotation)
MONITOR="$(dirname "$0")/monitor.sh"
# arguments for the sensors monitor
MONITOR_ARGS=

# screen notify command, just comment out if it is not need
NOTIFY="notify-send"


# name of an input device to configure (from xinput)
INPUTDEV="Elan Touchscreen"
# name of a transformation matrix property of the device
MATRIX_PROP="Coordinate Transformation Matrix"
# name of a dead edges sizes property of the device
# by this property you can disable keyboard area from taps
# use it if the screen keyboard handle touches by it self directly from a device
# just comment out this if you don't need this feature
EDGE_PROP="Trackpad Edge Sizes"
# height of a dead zone area at the bottom of the screen
# integer value - count of percents of the screen height
KEYAREA=20



function usage() {
    echo ""
    echo "Usage ./rotate.sh [command] ..."
    echo "  available commands:"
    echo "    kbon     - open screen keyboard"
    echo "    kboff    - close screen keyboard"
    echo "    kbtoggle - toggle screen keyboard"
    echo "    aron     - enable auto-rotation"
    echo "    aroff    - disable auto-rotation"
    echo "    artoggle - toggle auto-rotation"
    echo "    quiet    - disable screen notify"
    echo "    normal,left,inverted,right"
    echo "             - comma separated looped sequence of orientations"
    echo "               screen will be turned to the next state relative to current"
    echo "               if only one item is in sequence"
    echo "                   then screen will be turned into it"
    echo "               if current state is not present in the sequence"
    echo "                   then the first sequence item will be chosen"
    echo "  without args script will just configure the input device for the current orientation"
    echo ""
}



# function gets a next item from the looped sequence
# example: next normal,left,inverted,right left
# will be echoed: inverted
function next() {
    local res="$(echo "$1" | tr -s , '\n' | grep -i -x -A 1 "$2" | tail +2)"
    if [ -z "$res" ]; then
        res="$(echo "$1" | tr -s , '\n' | head -1)"
    fi
    echo "$res"
}


# always returns a valid name of the rotation (bad values will replaced with 'normal')
function fix_rotation_name() {
    case "$1" in
    right | inverted | left) echo "$1";;
    *) echo "normal";;
    esac
}
# check if the first arg is a comma separated sequence of valid rotation names
function is_rotation_sequence() {
    local s="$(echo "$1" | tr -s , ' ')"
    local x=
    for x in $s; do
        [ "$x" == "$(fix_rotation_name "$x")" ]
    done
}


# show the current screen rotation
function current_rotation() {
    fix_rotation_name "$(xrandr --screen 0 --current --query | head -2 | tail -1 | cut -d' ' -f5)"
}
# turn screen to chosen rotation
# possible values: normal, left, inverted and right
function rotate_screen() {
    xrandr -o "$1"
}


# check if our serivice is running
function is_run() {
    [ -f "$PIDS/$1.pid" ]
}
# run our service if it is not running
# args: service-name command command-args...
function run() {
    local svc="$1"
    shift
    ( ! is_run "$svc" ) || return 0
    [ -n "$1" ] || return 0
    nohup "$@" &> /dev/null &
    local p="$!"
    mkdir -p "$PIDS"
    echo "$p" > "$PIDS/$svc.pid"
    [ "$svc" != "monitor" ] || [ -z "$NOTIFY" ] || "$NOTIFY" "rotation ON"
}
# stop our service
function stop() {
    is_run "$1" || return 0
    kill "$(cat "$PIDS/$1.pid")" || true
    rm "$PIDS/$1.pid"
    [ "$1" != "monitor" ] || [ -z "$NOTIFY" ] || "$NOTIFY" "rotation OFF"
}
# stop our service if it is running or run it in other case
# args: service-name command command-args...
function toggle() {
    [ -n "$1" ] || return 0
    if is_run "$1"; then stop "$1"; else run "$@"; fi
}


# configure the input device for a specified rotation
function configure_input() {
    local rot="$(current_rotation)"
    local matrix=
    local edges=
    local area=0
    if is_run keyboard; then area="$KEYAREA"; fi

    echo "configure the input device for orientation: $rot"
    case "$rot" in
    left)
        matrix="0 -1 1 1 0 0 0 0 1"
        edges="0 0 0 $area"
        ;;
    right)
        matrix="0 1 0 -1 0 1 0 0 1"
        edges="0 0 $area 0"
        ;;
    inverted)
        matrix="-1 0 1 0 -1 1 0 0 1"
        edges="$area 0 0 0"
        ;;
    *)
        matrix="1 0 0 0 1 0 0 0 1"
        edges="0 $area 0 0"
        ;;
    esac

    [ -z "$INPUTDEV" ] || [ -z "$MATRIX_PROP" ] || \
        xinput --set-prop "$INPUTDEV" "$MATRIX_PROP" $matrix
    [ -z "$INPUTDEV" ] || [ -z "$EDGE_PROP" ] || [ -z "$KEYAREA" ] || \
        xinput --set-prop "$INPUTDEV" "$EDGE_PROP" $edges
}



# parse arguments
SHOW_USAGE=
CHANGED=
while [ "$#" != "0" ]; do
  IS_CMD=1
  if [ "$1" = "kbon" ]; then
    echo "open screen keyboard"
    run keyboard "$KEYBOARD" $KEYBOARD_ARGS
  elif [ "$1" = "kboff" ]; then
    echo "close screen keyboard"
    stop keyboard
  elif [ "$1" = "kbtoggle" ]; then
    echo "toggle screen keyboard"
    toggle keyboard "$KEYBOARD" $KEYBOARD_ARGS
  elif [ "$1" = "aron" ]; then
    echo "run auto-rotation sensor monitor"
    run monitor "$MONITOR" $MONITOR_ARGS
  elif [ "$1" = "aroff" ]; then
    echo "stop auto-rotation sensor monitor"
    stop monitor
  elif [ "$1" = "artoggle" ]; then
    echo "toggle auto-rotation sensor monitor"
    toggle monitor "$MONITOR" $MONITOR_ARGS
  elif [ "$1" = "quiet" ]; then
    NOTIFY=
  elif is_rotation_sequence "$1"; then
    ROT="$(current_rotation)"
    ROT="$(next "$1" "$ROT")"
    echo "turn screen to: $ROT"
    rotate_screen "$ROT"
  else
    echo "unknown command: $1"
    IS_CMD=
  fi

  if [ -n "$IS_CMD" ]; then CHANGED=1; else SHOW_USAGE=1; fi
  shift
done

[ -z "$SHOW_USAGE" ] || usage
( [ -n "$SHOW_USAGE" ] && [ -z "$CHANGED" ] ) || configure_input
[ -z "$SHOW_USAGE" ]
echo success