#!/usr/bin/env bash
# License: GPU GPL v3
# Settings
# --------
# It's probably a good idea to verify these prior to first use of the script and
# modify if necessary.
# IIO devices path (Linux industrial I/O subsystem)
# One probably does not need to change this
# iio_devices_path - path to the iio devices buffer tree
# iio_devices_name - the file containing device name
iio_devices_path="/sys/bus/iio/devices/iio:device"
iio_devices_name="name"
# Accelerometer device path and sensor files
# accel_device_name - what we expect to find in the ../iio:deviceX/name file
# accel_device_path - will be populated by identify_device(), may be set to override
# accel_sensor_x - the channel file of the axis to check for screen rotation (x,y,z)
accel_device_name="accel_3d"
accel_device_path=""
accel_sensor_x="in_accel_x_raw"
accel_sensor_y="in_accel_y_raw"
accel_sensor_z="in_accel_z_raw"
# Current display that will be rotated. This will be populated automatically
# with the name of connected output (xrandr) by the script if left empty.
# Otherwise one can fill this in manually to omit automatical detection
xdisplay=""
# Input devices (touchpad and touchscreen)
# One probably needs to run xinput --list to verify the names of the input devices
# actually present and modify these variables accordingly. Leaving one of them blank
# will omit rotation for that device.
touchpad=''
touchscreen='SYNAPTICS Synaptics Touch Digitizer V04'
# Error codes
error_arguments=1 # Incorrect argument(s) either to the script or a function
error_device=2 # Accelerometer device path or folder structure not found?
error_sensor=3 # Accelerometer sensor channel file $accel_sensor not found?
# Functions
# ---------
show_help() {
local NAME
NAME=$(basename "$0")
cat <<EOF
$NAME is a shell script that will check the IIO accelerometer sensor and turn to tablet or laptop mode: set the screen and touchscreen orientation according to rotation, launch onboard keyboard.
Usage: $NAME [--cont [<interval>]] [--help]
-h, --help Print this help and quit
-c, --cont Run continuously at specified interval (default 1s)
Example: $NAME --cont 2 & # Run the script in the background at 2s intervals
NOTE: The script has only been tested on Acer Aspire R13 (R7-371T) transformer laptop.
EOF
}
identify_device() {
# Will look for the device whose name is given as the only argument,
# by comparing the devices names in /sys/bus/iio/devices/iio:deviceX/name
# file and return the path to the device folder.
# Relies on the devices_path variable set in the Setting section.
# Process arguments:
if [[ $# -eq 0 ]]; then
# Incorrect arguments
exit ${error_arguments}
else
local device_name="${1}"
fi
# Loop through all devices and check names:
local device
for device in "${iio_devices_path}"* ; do
if [[ $(cat "${device}/${iio_devices_name}") == "${device_name}" ]]; then
echo "${device}"
fi
done
}
read_accelerometer() {
# The first argument is obligatory and should specify one axis
# of the accelerometer device: -x, -y or -z
# Also --raw will not try to decode the reading.
# Process arguments:
if [[ $# -eq 0 ]]; then
# Incorrect arguments
exit ${error_arguments}
else
while [[ "$*" != "" ]]; do
local accel_sensor=${accel_sensor_x}
case "$1" in
# Check for --raw option
"-r"|"--raw" ) local option_raw=1 ; shift ;;
# Select accelerometer axis (-x is default)
"-x" ) shift ;;
"-y" ) accel_sensor=${accel_sensor_y} ; shift ;;
"-z" ) accel_sensor=${accel_sensor_z} ; shift ;;
# Ignore any other options
* ) shift ;;
esac
done
fi
# Read the sensor:
if [[ -f "${accel_device_path}/${accel_sensor}" ]]; then
local reading ; reading=$(cat "${accel_device_path}/${accel_sensor}")
else
# No sensor file found?
exit ${error_sensor}
fi
# Decode the reading
# It's a 16-bit signed integer, but reads as a string
# 1..1000 : positive angle (0-90°)
# 65575..54576 : negative angle (0-90°)
local max_reading=1000
local max_positive=32767
local max_integer=65535
# Decode reading into degrees (unless option_raw in set)
if [[ -z ${option_raw} ]]; then
if [[ ${reading} -gt ${max_positive} ]]; then
echo "$(((reading - max_integer) / (max_reading / 90)))"
else
echo "$((reading / (max_reading / 90)))"
fi
else
echo "${reading}"
fi
}
rotate_screen() {
# Will try to rotate the display and input devices, using:
# xrandr to rotate the display picture;
# xinput to rotate the touchpad and touchscreen (via Coordinate Transformation Matrix).
# Relies on touchpad and touchscreen variables set in the Settings section.
# Rotate the display:
xrandr --output $1 --rotate $2
# Rotate the input devices
transform='Coordinate Transformation Matrix'
case "$2" in
normal)
[ ! -z "$touchpad" ] && xinput set-prop "$touchpad" "$transform" 1 0 0 0 1 0 0 0 1
[ ! -z "$touchscreen" ] && xinput set-prop "$touchscreen" "$transform" 1 0 0 0 1 0 0 0 1
;;
inverted)
[ ! -z "$touchpad" ] && xinput set-prop "$touchpad" "$transform" -1 0 1 0 -1 1 0 0 1
[ ! -z "$touchscreen" ] && xinput set-prop "$touchscreen" "$transform" -1 0 1 0 -1 1 0 0 1
;;
left)
[ ! -z "$touchpad" ] && xinput set-prop "$touchpad" "$transform" 0 -1 1 1 0 0 0 0 1
[ ! -z "$touchscreen" ] && xinput set-prop "$touchscreen" "$transform" 0 -1 1 1 0 0 0 0 1
;;
right)
[ ! -z "$touchpad" ] && xinput set-prop "$touchpad" "$transform" 0 1 0 -1 0 1 0 0 1
[ ! -z "$touchscreen" ] && xinput set-prop "$touchscreen" "$transform" 0 1 0 -1 0 1 0 0 1
;;
esac
}
main() {
# Process arguments
while [[ "$*" != "" ]]; do
case "$1" in
"-h"|"--help" )
show_help
exit 0
;;
"-c"|"--cont" )
# Check next argument for repeat interval
option_cont=1
shift
if [[ ! -z "$1" && -z "${1//[0-9.]/}" ]]; then
option_cont="$1"
shift
fi
;;
* )
;;
esac
done
# Identify the current display to be rotated
if [[ "${xdisplay}" == "" ]]; then
xdisplay=`xrandr --current | grep " connected" | sed -e 's/ .*//g'`
fi
# Identify the accelerometer device, unless the accel_device_path has been set manually
if [[ "${accel_device_path}" == "" ]]; then
accel_device_path=$(identify_device ${accel_device_name})
fi
# Run the test
while true ; do
# Get the screen rotation angle from the accelerometer sensor
angle_x=$(read_accelerometer -x)
angle_y=$(read_accelerometer -y)
angle_z=$(read_accelerometer -z)
# Check orientation
if [[ ${angle_z//-/} -lt 80 && ${angle_x} -gt 20 ]]; then
echo "Screen orientation: left (x=${angle_x}, y=${angle_y}, z=${angle_z})"
rotate_screen ${xdisplay} left
elif [[ ${angle_z//-/} -lt 80 && ${angle_x} -lt -20 ]]; then
echo "Screen orientation: right (x=${angle_x}, y=${angle_y}, z=${angle_z})"
rotate_screen ${xdisplay} right
elif [[ ${angle_y//-/} -gt 20 && ${angle_x//-/} -lt 20 ]]; then
echo "Screen orientation: normal (x=${angle_x}, y=${angle_y}, z=${angle_z})"
rotate_screen ${xdisplay} normal
else
echo "Screen orientation: keep (x=${angle_x}, y=${angle_y}, z=${angle_z})"
fi
# Run continuously (option_cont)?
if [[ "${option_cont}" == "" ]]; then
exit 0
else
sleep ${option_cont}
fi
done
}
main "$@"