#!/bin/bash

# Create a minimal xorg.conf that sets the resolution and/or the video driver.
#
# If our name is "buildxconfig" then we mimic the behavior of the older version.
# Otherwise we behave better and take all of our input via the command line.
#

ME=${0##*/}

DO_XORG=

DRIVER_DIR=/usr/lib/xorg/modules/drivers
DRIVER_EXT=_drv.so

DEFAULT_DRIVER="vesa"
test -e /sys/firmare/efi  && DEFAULT_DRIVER="fbdev"

 WHITE=$(printf "\e[1;37m")
   RED=$(printf "\e[0;31m")
YELLOW=$(printf "\e[1;33m")
    NC=$(printf "\e[0m")


usage() {
    local ret=${1:-0}
    cat <<Usage
Usage: $ME [options] [<driver>,uxa,sna,busid=<busid>]

Create an xorg.conf file based on the arguments given on the command
line.

Later parameters override earlier ones if what they do overlaps.

    $ME fbdev,vesa

NOTE: the default driver is "vesa" when booting legacy and "fbdev" when
bootig UEFI.

Options:
    -f --force          Force creation even if driver specfied is not found
    -h --help           Show this usage
    -o --output=<file>  Write output to <file> instead of stdout
    --                  Optional delimiter to indicate the end of options

Xorg Arguments:

    uxa|sna            Use Intel driver with the specified acceleration
    busid=<busid>      Set PCI Bus-ID to <busid>
    raw-busid=<busid>  Set Bus-IS exactly as given
    safe               Set driver to default-driver (for compatibility)
    default            Set driver to default-driver (for compatibility)

Any other argument is considered to be the name of a graphics driver.
Use --force to create an xorg.conf that specifies a driver that is not
on the host system.

Available drivers:
$(valid_drivers | column -c 70)

Usage
    exit $ret
}

main() {

    local ORIG_ARGS="$*"
    local short_stack="fho"

    while [ $# -gt 0 -a -n "$1" -a -z "${1##-*}" ]; do
        local arg=${1#-} ; shift
        local value=${arg#*=}

        #--- unstack stacked single-letter options ---
        case $arg in
            [$short_stack][$short_stack]*)
                if echo "$arg" | grep -q "^[$short_stack]\+$"; then
                    set -- $(echo $arg | sed -r 's/([a-zA-Z])/ -\1 /g') "$@"
                    continue
                fi;;
        esac

        case $arg in
              -force|f) FORCE=true                   ;;
               -help|h) usage                        ;;
             -output|o) need_param  "-$arg" "$@"
                        OUT_FILE=$1 ; shift          ;;
             -output=*) OUT_FILE=$value              ;;
                     -) break                        ;;

             *) fatal "Unknown parameter %s" "-arg$" ;;
        esac
    done

    local param
    for param in ${*//,/ };  do
        value=${param#*=}

        case $param in
                 busid=*) BUS_ID=$(convert_bus_id "$value")  ;;
             raw-busid=*)  BUS_ID=$value         ;;
             composite|c) ADD_COMPOSITE=true     ;;
            safe|default)                        ;;
                 uxa|sna) ADD_INTEL_ACCEL=$value
                           DRIVER=intel          ;;
                      *)  DRIVER=$param          ;;
        esac
    done

    if [ -z "$FORCE" -a -n "$DRIVER" ]; then
        test -e $DRIVER_DIR/$DRIVER$DRIVER_EXT || driver_error $DRIVER
    fi

    if [ "$OUT_FILE" ]; then
        mkdir -p $(dirname "$OUT_FILE")
        [ -e "$OUT_FILE" -a ! -e "$OUT_FILE.bak" ] && mv $OUT_FILE $OUT_FILE.bak
        write_xorg_conf "$RESOLUTION" "$DRIVER" > $OUT_FILE
    else
        write_xorg_conf "$RESOLUTION" "$DRIVER"
    fi
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
driver_entry() {
    local driver=$1
    [ "$driver" ] || return

    local newline="\n"
    if [ "$DRIVER_ERROR" ]; then
        driver=$DEFAULT_DRIVER
        newline=""
        echo ; echo
        cat<<Driver_Error
    #=========================================================================
    # WARNING: the "$DRIVER_ERROR" driver was not found.  Using "$driver" instead.
    # Use the --force option to force the use of any driver.
    #=========================================================================
Driver_Error
    fi

    printf "$newline    Driver     \"$driver\""
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
write_xorg_conf() {
    local mode=$1  driver=$2

    cat <<Xorg_Conf
#-----------------------------------------------------------------------------
# xorg.conf file
#
# Generated by $ME sometime around $(date)
#
# If you want to save customizations, delete the line above or this
# file will get automatically deleted on the next live boot.
#
# Command line parameters: $ORIG_ARGS
#-----------------------------------------------------------------------------

Section "Monitor"
    Identifier "Monitor0"
    Option "DPMS" "true"
EndSection

Section "Device"
    Identifier "Device0"$(driver_entry $driver)$(add_intel_accel)$(add_bus_id)
EndSection$(composite_section $ADD_COMPOSITE)

Section "Screen"
    Identifier      "Screen0"
    Monitor         "Monitor0"
    Device          "Device0"
EndSection
Xorg_Conf
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
composite_section() {
    local flag=$1
    [ -n "$flag" ] || return
    echo ; echo
    cat<<Composite_Section
Section "Extensions"
   Option "Composite" "Enable"
EndSection
Composite_Section
}

#------------------------------------------------------------------------------
# Make sure there are at least 2 input paramters and make sure the 2nd one
# does not start with a "-".
#------------------------------------------------------------------------------
need_param() {
    [ $# -lt 2 ] && fatal "A parameter is required after option %s" "$1"
    [ -n "$2" -a -z "${2##-*}" ] && fatal "Suspicious parameter after option %s" "$1"
}

add_intel_accel() {
    [ "$ADD_INTEL_ACCEL" ] || return
    echo -e "\n    Option     \"AccelMethod\"  \"$ADD_INTEL_ACCEL\""
}

add_bus_id() {
    [ "$BUS_ID" ] || return
    BUS_ID=$(echo $BUS_ID | sed -r "s/\.([0-9a-f]+)$/:\1/")
    echo -e "\n    BusID      \"$BUS_ID\""
}


#------------------------------------------------------------------------------
# Convert a bus-id from lspci to the format used in xorg.conf
# Convert from hex to decimal, add PCI prefix, and use" :" instead of "."
#------------------------------------------------------------------------------
convert_bus_id() {
    printf "PCI:%02d:%02d:%d\n" $(echo $1 | sed -r "s/([0-9a-f]+):([0-9a-f]+)[:.]([0-9a-f]+)/0x\1 0x\2 0x\3/i" )
}

driver_error() {
    local driver=$1
    DRIVER_ERROR=$driver
    cat <<Driver_Error >&2
    $WHITE$ME$YELLOW Warning:$RED Unrecognized (uninstalled?) video driver:$WHITE $driver$NC
    ${WHITE}Using $DEFAULT_DRIVER instead$NC
Driver_Error
}

valid_drivers() {
    ls $DRIVER_DIR | sed -n "s/$DRIVER_EXT//p"
}
#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
fatal() {
    local fmt=$1 ; shift;
    printf "$WHITE$ME$YELLOW Error:$RED $fmt$NC" "$@" >&2
    exit 7
}

warn() {
    echo "$ME$YELLOW Warning: $*$NC" >&2
}

main "$@"
