#!/bin/sh
# x-rotate-display --- rotate display, adjust mice and trackpads
# Author: Noah Friedman <friedman@splode.com>
# Created: 2012-07-22
# Public domain

# $Id: x-rotate-display,v 1.6 2019/01/10 10:49:47 friedman Exp $

get_current_parameters()
{
    set x `xrandr --screen $screen \
             | sed -e '/ connected/!d' \
                   -e 's/ (.*//' \
                   -e 's/\([0-9]*\)x\([0-9]*\)[0-9+---]*/\1 \2/' \
                   -e 's/ primary / /' \
                   -e 's/ connected//' \
                   -e q`

    case $# in
        5 ) : ;;
        4 ) set "$@" normal ;;
        * ) echo "Can't determine current settings properly.  Aborting." 1>&2
            exit 1 ;;
    esac
    shift

    output=$1 c_xres=$2 c_yres=$3
    case $4 in
        normal   ) c_orient=0 ;;
        left     ) c_orient=1 ;;
        inverted ) c_orient=2 ;;
        right    ) c_orient=3 ;;
    esac
}

compute_new_orientation()
{
    case $1 in
        0 | normal   ) n_orient=0 ;;
        1 | left     ) n_orient=1 ;;
        2 | inverted ) n_orient=2 ;;
        3 | right    ) n_orient=3 ;;

        l | lturn    ) n_orient=$((((4 + $c_orient) + 1) % 4)) ;;
        r | rturn    ) n_orient=$((((4 + $c_orient) - 1) % 4)) ;;
        f | flip     ) n_orient=$((((4 + $c_orient) - 2) % 4)) ;;

        t | *tablet )
            case $c_orient in
                0 ) n_orient=1 ;; # normal -> left
                * ) n_orient=0 ;; #    any -> normal
            esac ;;

        * ) echo "$1: invalid argument" 2>&1
            exit 1 ;;
    esac
}

calibrate_evdev()
{
    orientation=$1
    shift

    case $orientation in
        0 | normal   ) swap=0 invert_x=0 invert_y=0 ;;
        1 | left     ) swap=1 invert_x=1 invert_y=0 ;;
        2 | inverted ) swap=0 invert_x=1 invert_y=1 ;;
        3 | right    ) swap=1 invert_x=0 invert_y=1 ;;
    esac

    for device in "$@"; do
        xinput set-prop "$device" "Evdev Axes Swap"      $swap
        xinput set-prop "$device" "Evdev Axis Inversion" $invert_x $invert_y
    done
}

# Note that some CTM won't be effective for some pointers unless they are
# put into ABSOLUTE mode first.
calibrate_ctm()
{
    orientation=$1
    shift

    case $orientation in
        0 ) matrix=' 1  0  0    0  1  0    0  0  1' ;;
        1 ) matrix=' 0 -1  1    1  0  0    0  0  1' ;;
        2 ) matrix='-1  0  1    0 -1  1    0  0  1' ;;
        3 ) matrix=' 0  1  0   -1  0  1    0  0  1' ;;
    esac

    for device in "$@"; do
        xinput set-prop "$device" "Coordinate Transformation Matrix" $matrix
    done
}

pointer_ids()
{
    xinput list | sed -ne '/ pointer /!d' -e 's/.*id=\([0-9]*\).*/\1/p'
}

xrandr1()
{
    xrandr --screen $screen --output $output "$@"
}

max()
{
    max=$1
    shift
    for n in "$@"; do
        if [ $n -gt $max ]; then
            max=$n
        fi
    done
    echo $max
}

rotate()
{
    case $n_orient in
        0 ) s_orient=normal   ;;
        1 ) s_orient=left     ;;
        2 ) s_orient=inverted ;;
        3 ) s_orient=right    ;;
    esac

    # 2014-11-23 (still current 2015-10-04)
    # tigervnc module will crash the X server when rotating unless the
    # display is the same dimension on both axes.  To work around this, we
    # temporarily change the display to be square (larger in both
    # directions) and then set it back to its new proper size.
    if xdpyinfo | grep -q VNC; then
        max=`max $c_xres $c_yres`
        xrandr1 --fb ${max}x${max} --rotate $s_orient

        case $c_orient:$n_orient in
            0:2 | 2:0 | 1:3 | 3:1 ) xrandr1 --fb ${c_xres}x${c_yres} ;;
            *                     ) xrandr1 --fb ${c_yres}x${c_xres} ;;
        esac
    else
        xrandr1 --rotate $s_orient
    fi
}

main()
{
    direction=${1-rturn}
    screen=${2-0}

    get_current_parameters  $screen
    compute_new_orientation $direction

    case $n_orient in
        $c_orient ) return 0 ;;
    esac

    for id in `pointer_ids`; do
        props=`xinput list-props $id`
        case $props in
            *'Coordinate Transformation Matrix'* )
                 calibrate_ctm   $n_orient $id ;;
            *'Evdev Axes Swap'* )
                calibrate_evdev $n_orient $id ;;
        esac
    done

    rotate
}

main "$@"

# eof
