#!/bin/sh -f

PATH="/usr/lib/alterator-x11:$PATH"

DATADIR="/usr/share/alterator-x11"

alterator_api_version=1

. alterator-sh-functions
. shell-quote

XORGDRV_DIR="$(getconf LIBDIR)/X11/modules/drivers"
XORG_CONF_DIR="/etc/X11/xorg.conf.d"
CONF_FILE="10-monitor.conf"
XORG_MONITOR_CONF="$XORG_CONF_DIR/$CONF_FILE"
CACHE_DIR="/var/cache/alterator/x11"
CACHED_CONF="$CACHE_DIR/$CONF_FILE"
XSETUP="/usr/bin/xsetup"
XSETUP_MONITOR="/usr/bin/xsetup-monitor"
MODULES_BLACKLIST="/etc/modprobe.d/blacklist-alterator-x11.conf"
CONFIG_HEADER="# Generated automatically, do not edit manually."
NEED_MAKEINITRD=

# For backward compatibility only.
XORG_CONF_COMPAT=
XCONFTOOL="/usr/bin/xconf"
SETUPDRVTOOL="/usr/bin/x11setupdrv"
XORGCONFIG_MAIN="/etc/X11/xorg.conf"
XORGCONFIG_TEMPLATE="$DATADIR/xorg.conf"
XORGCONFIG_WORKCOPY="/etc/X11/xorg.conf.alterator"
###

# cache functions
init_cache()
{
	# remove prev data
	rm -rf "$CACHE_DIR"
	mkdir -p "$CACHE_DIR"

	[ -f "$XORG_MONITOR_CONF" ] && cp -a  "$XORG_MONITOR_CONF" "$CACHED_CONF"
}

commit_cache()
{
	if [ -s "$CACHED_CONF" ]; then
		cp -a  "$CACHED_CONF" "$XORG_MONITOR_CONF"
	else
		rm -f "$XORG_MONITOR_CONF"
	fi
	# remove config-x11's backup to prevent him from overwriting new xorg.conf
	rm -f /tmp/alterator/config-x11.backup
}

#### xorg config functions
commit_workcopy()
{
  [ -f "$XORGCONFIG_WORKCOPY" ] && cp -f "$XORGCONFIG_WORKCOPY" "$XORGCONFIG_MAIN"
  if [ -x "$SETUPDRVTOOL" ]; then
    "$SETUPDRVTOOL" "$XORGCONFIG_MAIN" >&2 #setup correct GL symlinks for the current config file
  fi
  # remove config-x11's backup to prevent him from overwriting new xorg.conf
  rm -f /tmp/alterator/config-x11.backup
}


# xdepth and xres depends on selected driver

get_auto_xres(){
  local xdriver="$1"
  if [ "$xdriver" = "fbdev" -a -c "/dev/fb0" ]; then
    echo "$(fbresolution:-"auto")"
    return
  fi
  # recommended resolution
  auto_xresolution="$(monitor_ddc resbest)"
  [ -n "$auto_xresolution" ] || auto_xresolution="auto"
  echo "$auto_xresolution"
}

#autodetect information
read_autodetect_data()
{
	#video data
	monitor_ddc clean
	local data="$(video_scan)"

	auto_cardname="$(video_scan -s name)"
	auto_xdriver="$(video_scan -s first)"
	auto_xdrivername="$(video_drv -s desc "$auto_xdriver")"

	#monitor data
	auto_monitor="Monitor0"

	local fbres=
	[ -c "/dev/fb0" ] && fbres=$(fbresolution)
	if [ -z "$auto_xdriver" -a -n "$fbres" ]; then
	   auto_xdriver="fbdev"
	   return
	fi

	[ -n "$auto_xdriver" ] || auto_xdriver="vesa"
}

#### get functions
get_monitor()
{
    echo "$auto_monitor"
}

get_xdriver()
{
    local xdriver=
    if [ -n "$XORG_CONF_COMPAT" ]; then
        xdriver="$($XCONFTOOL -d "$XORGCONFIG_WORKCOPY")"
    elif [ -s "$CACHED_CONF" ]; then
        xdriver="$($XSETUP -s Device -e Driver "$CACHED_CONF")"
    fi

    echo "${xdriver:-auto}"
}

get_display_width()
{
    local display_width=
    display_width=`$XSETUP -s Monitor -e DisplaySize "$CACHED_CONF" | sed 's|\([[:digit:]][[:digit:]]*\)[[:space:]][[:space:]]*[[:digit:]].*$|\1|'`
    echo "${display_width:-0}"
}

get_display_height()
{
    local display_height=
    display_height=`$XSETUP -s Monitor -e DisplaySize "$CACHED_CONF" | sed 's|^.*[[:space:]]\([[:digit:]][[:digit:]]*\).*$|\1|'`
    echo "${display_height:-0}"
}

get_xdepth()
{
    local xdepth=
    if [ -n "$XORG_CONF_COMPAT" ]; then
        xdepth="$($XCONFTOOL -c "$XORGCONFIG_WORKCOPY")"
    elif [ -s "$CACHED_CONF" ]; then
        xdepth="$($XSETUP -s Screen -e DefaultDepth "$CACHED_CONF")"
    fi

    echo "${xdepth:-auto}"
}

get_xresolution()
{
    local xresolution=
    if [ -n "$XORG_CONF_COMPAT" ]; then
        xresolution="$($XCONFTOOL -r "$XORGCONFIG_WORKCOPY")"
    elif [ -s "$CACHED_CONF" ]; then
        xresolution="$($XSETUP -s Screen -S Display -e Modes "$CACHED_CONF" | cut -f1 -d' ')"
    fi

    echo "${xresolution:-auto}"
}

get_xdrivername()
{
    local xdriver="$1"
    local desc=

    if [ -z "$xdriver" -o "$xdriver" = auto ]; then
        desc="`_ "automatically"`"
    else
        desc="$(video_drv -s desc "$xdriver")"
    fi

    [ -n "$desc" ] || desc="$xdriver"

    echo "$desc"
}

#### list functions
print_recommended()
{
    [ "$1" = "$2" ] && printf -- ' - %s' "`_ "recommended"`"
}

print_xdepth()
{
    write_enum_item "$1" "$2$(print_recommended "$1" "$3")"
}

list_xdepth()
{
    local xdriver="$(get_xdriver)"
    local xdepth_list="32 24 16 15 8"
    local recomm_xdepth=

    [ -n "$xdriver" ] || xdriver="$auto_xdriver"

    if [ -n "$XORG_CONF_COMPAT" ]; then
        recomm_xdepth=$(video_drv -s depth "$xdriver")
	else
        print_xdepth "auto" "`_ "automatically"`" "auto"
	fi
    if [ "$xdriver" != auto ]; then
        xdepth_list=$(video_drv -s dlist "$xdriver")
    fi

    for i in $xdepth_list; do
	case $i in
	    8) print_xdepth 8 "`_ "256 colors (8 bit)"`" $recomm_xdepth;;
	    15) print_xdepth 15 "`_ "32 thousand colors (15 bits)"`" $recomm_xdepth ;;
	    16) print_xdepth 16 "`_ "65 thousand colors (16 bits)"`" $recomm_xdepth ;;
	    24) print_xdepth 24 "`_ "16 million colors (24 bits)"`" $recomm_xdepth ;;
	    32) print_xdepth 32 "`_ "4 billion colors (32 bits)"`" $recomm_xdepth ;;
	     *) print_xdepth $i "$i `_ "bits"`" $recomm_xdepth ;;
	esac
    done
}

list_xresolution()
{
    local res="$(monitor_ddc reslist)"
    [ -n "$res" ] || res="$(cat "$DATADIR/resolutions")"

    local recommended_xres=$(get_auto_xres )

    write_enum_item "auto" "`_ "automatically"`"
    for i in $res;do
	write_enum_item "$i" "$i$(print_recommended "$i" "$auto_xresolution")"
    done
}

list_xdriver()
{
    local IFS='	'

    [ -n "$XORG_CONF_COMPAT" ] ||
        write_enum_item "auto" "$(get_xdrivername auto)$(print_recommended auto auto)"
    find "$XORGDRV_DIR" -name '*_drv.so'|
	sed -r 's,.*/([a-z0-9]+)_drv\.so$,\1,'| sort |
	while read driver rest; do
	    description="$(get_xdrivername "$driver")"
	    write_enum_item "$driver" "$driver - $description$(print_recommended "$driver" "${XORG_CONF_COMPAT:+$auto_xdriver}")"
	done
}

#### exit handler and initialization

exit_handler()
{
	trap - TERM HUP EXIT

	if [ -x "$SETUPDRVTOOL" -a -f "$XORGCONFIG_MAIN" ]; then
          "$SETUPDRVTOOL" >&2
        fi
	rm -f "$XORGCONFIG_WORKCOPY"
	rm -rf "$CACHE_DIR"
}

#### write functions
check_xdepth()
{
    local xdriver="$1"; shift
    local xdepth="$1"; shift

    [ "$xdriver" = auto -o "$xdepth" = auto ] && return 0

    local xdepth_list="$(video_drv -s dlist "$xdriver")"
    for d in $xdepth_list; do
        [ "$d" = "$xdepth" ] && return 0
    done

    return 1
}

write_monitor()
{
    local monitor="$1"; shift

	$XSETUP -s Monitor -e Identifier -v "$monitor" "$CACHED_CONF"
}

write_xdriver()
{
    local xdriver="$1"; shift

    sed -ri "/^[[:blank:]]*Section[[:blank:]]+\"Device\"/,/^[[:blank:]]*EndSection/{/^[[:blank:]]*Driver/d}" "$CACHED_CONF"

    if [ -n "$xdriver" -a "$xdriver" != auto ]; then
        $XSETUP_MONITOR -d "$xdriver" "$CACHED_CONF"
    fi
}

write_depth()
{
    local xdepth="$1"
    if [ -n "$xdepth" -a "$xdepth" != auto ]; then
        $XSETUP_MONITOR -c "$xdepth" "$CACHED_CONF"
    else
		$XSETUP -d -s Screen -e DefaultDepth "$CACHED_CONF"
		$XSETUP -d -s Screen -S Display -e Depth "$CACHED_CONF"
    fi
}

write_resolution()
{
    local xresolution="$1"; shift
    local monitor="$1"; shift
    local xdepth="$1"; shift

    [ -n "$monitor" ] || monitor="$(get_monitor)"
    [ -n "$xresolution" ] || xresolution="$(get_xresolution)"
    [ -n "$xdepth" ] || xdepth="$(get_xdepth)"

    local old_xresolution="$($XSETUP -s Screen -S Display -e Modes "$CACHED_CONF" | cut -f1 -d' ')"
    if [ -z "$old_xresolution" -a "$xresolution" = auto ] || [ "$old_xresolution" = "$xresolution" -a \
            "$xdepth" = "$($XSETUP -s Screen -e DefaultDepth "$CACHED_CONF")" -a \
            "$xdepth" = "$($XSETUP -s Screen -S Display -e Modes "$CACHED_CONF" | cut -f1 -d' ')" ]; then
        return 0
    fi

    if [ "$xresolution" = auto ]; then
        $XSETUP -d -s Screen "$CACHED_CONF"
        return 0
    fi

    $XSETUP_MONITOR -r "$xresolution" "$CACHED_CONF"
    write_depth "$xdepth"
}

write_displaysize()
{
    local displaysize_use="$1"; shift
    local display_width="$1"; shift
    local display_height="$1"; shift
    if [ "$displaysize_use" == "#t" ] && ((display_width>50)) && ((display_height>30)) ; then
	$XSETUP -s Monitor -e DisplaySize -v "$display_width $display_height" "$CACHED_CONF"
    else
	$XSETUP -s Monitor -e DisplaySize -d "$CACHED_CONF"
    fi
}

write_refresh_rate()
{
	local set_rrate="$1"; shift
	local xres="$1"; shift


	if [ "$set_rrate" = '#t' ]; then
		local tmp_hsync="$($XSETUP -s Monitor -e HorizSync "$CACHED_CONF")"
		local tmp_vrefr="$($XSETUP -s Monitor -e VertRefresh "$CACHED_CONF")"
		# Write generic values
		[ -n "$tmp_hsync" ] || $XSETUP_MONITOR --hsync 31.5 "$CACHED_CONF"
		[ -n "$tmp_vrefr" ] || $XSETUP_MONITOR --vrefr 50-61 "$CACHED_CONF"
	else
		$XSETUP -d -s Monitor -e HorizSync "$CACHED_CONF"
		$XSETUP -d -s Monitor -e VertRefresh "$CACHED_CONF"
	fi
}

is_need_makeinitrd()
{
    local new="$1"; shift
    local old="$1"; shift

    [ "$new" != "$old" ] || return 1
    for drv in "$new" "$old"; do
        case "$drv" in
            nv|nvidia|nouveau|nvidia|radeon|r128|ati|fglrx|vesa)
                return 0
            ;;
        esac
    done

    return 1
}

blacklist_one_module()
{
    [ -s "$MODULES_BLACKLIST" ] || echo "$CONFIG_HEADER" >"$MODULES_BLACKLIST"
    echo "blacklist $1" >>"$MODULES_BLACKLIST"
}

blacklist_modules()
{
    local xdriver="$1"; shift

    rm -f "$MODULES_BLACKLIST"
    case "$xdriver" in
        nv)
            blacklist_one_module nvidia
            blacklist_one_module nouveau
        ;;
        nouveau)
            blacklist_one_module nvidia
        ;;
        nvidia)
            blacklist_one_module nouveau
        ;;
        radeon|r128|ati)
            blacklist_one_module fglrx
        ;;
        fglrx)
            blacklist_one_module radeon
        ;;
        vesa)
            blacklist_one_module radeon
            blacklist_one_module fglrx
            blacklist_one_module nouveau
            blacklist_one_module nvidia
            blacklist_one_module i915
        ;;
    esac
}

####

trap exit_handler TERM HUP EXIT

[ -x /usr/sbin/x11presetdrv ] && /usr/sbin/x11presetdrv >&2 ||: #sure we use right nvidia.xinf files
read_autodetect_data #detect video hardware

# For backward compatibility only.
# If xorg.conf is exists then use it.
if [ -f "$XORGCONFIG_MAIN" ] ;then
  XORG_CONF_COMPAT=1
  # create initial workcopy
  cp -f "$XORGCONFIG_MAIN" "$XORGCONFIG_WORKCOPY"
else
  init_cache
fi

on_message()
{
	case "$in_action" in
		list)
			case "${in__objects##*/}" in
			    avail_xdepth)
				list_xdepth
				;;
			    avail_xresolution)
				list_xresolution
				;;
			    avail_xdriver)
				list_xdriver
				;;
			esac
		    ;;
		read)
			case "$in__objects" in
			    *)
				[ -f "/tmp/alterator/config-x11" ] && write_bool_param config_x11 yes

				#autodetect
				write_string_param auto_cardname "$auto_cardname"
				write_string_param auto_monitor "$auto_monitor"


                local xdriver="$(get_xdriver)"
                local xdepth="$(get_xdepth)"
                local xresolution="$(get_xresolution)"

				#fill missing values using autodetect data
				if [ -n "$XORG_CONF_COMPAT" -a "$xdriver" = auto ]; then
				  xdriver="$auto_xdriver"
				  video_setup "$xdriver" "$XORGCONFIG_WORKCOPY"
				fi
				if [ -n "$XORG_CONF_COMPAT" -a "$xdepth" = auto ]; then
				  xdepth="$(video_drv -s depth "$xdriver")"
				  xconf -C "$xdepth" "$XORGCONFIG_WORKCOPY" "$XORGCONFIG_WORKCOPY"
				fi
				if [ -n "$XORG_CONF_COMPAT" -a "$xresolution" = auto ]; then
				  xresolution="$(get_auto_xres "$xdriver")"
				  resolution_setup "$xresolution" "$XORGCONFIG_WORKCOPY"
				fi

				#calculate driver name
				write_string_param xdriver "$xdriver"
				write_string_param xdrivername "$(get_xdrivername "$xdriver")"
				write_string_param xdepth "$xdepth"
				write_string_param xresolution "$xresolution"
				if [ -n "$XORG_CONF_COMPAT" ]; then
					write_bool_param compat_mode true
					write_bool_param refresh_rate false
				else
					 write_bool_param compat_mode false
					 local tmp_hsync="$($XSETUP -s Monitor -e HorizSync "$CACHED_CONF")"
					 local tmp_vrefr="$($XSETUP -s Monitor -e VertRefresh "$CACHED_CONF")"
					 if [ -n "$tmp_hsync" -o -n "$tmp_vrefr" ]; then
						 write_bool_param refresh_rate true
					 else
						 write_bool_param refresh_rate false
					 fi
				fi
				# fill display size
				write_string_param xdisplay_width "$(get_display_width)"
				write_string_param xdisplay_height "$(get_display_height)"
				;;
			esac
			;;
		write)
			if [ -n "$XORG_CONF_COMPAT" -a ! -f "$XORGCONFIG_WORKCOPY" ];then
			    write_error "`_ "no workcopy was found"`"
			    return;
			fi

			case "$in__objects" in
			    /)
				local hsync= vrefr=
                if [ -n "$XORG_CONF_COMPAT" ]; then
					monitor_setup "$auto_monitor" "$XORGCONFIG_WORKCOPY"
                    [ -n "$in_xdepth" ] &&\
                      $XCONFTOOL -C "$in_xdepth" "$XORGCONFIG_WORKCOPY" "$XORGCONFIG_WORKCOPY"
                    [ -n "$in_xresolution" ] && resolution_setup "$in_xresolution" "$XORGCONFIG_WORKCOPY"
                    [ "$in_commit" = '#f' -o "$in_async" = 'yes' ] || commit_workcopy
                else
                    # Write resolution into xorg.conf.d/
                    write_resolution  "$in_xresolution" "$in_monitor" "$in_xdepth"
					write_refresh_rate "$in_refresh_rate" "$in_xresolution"
                fi

                # Write display size
                write_displaysize "$in_xdisplaysize_use" "$in_xdisplay_width" "$in_xdisplay_height"

                if [ "$in_commit" = '#t' ]; then
                    local xdrv="$(get_xdriver)"
                    blacklist_modules "$xdrv"
                    [ -z "$NEED_MAKEINITRD" ] || make-initrd >/dev/null 2>&1
                    if [ "$xdrv" = auto -a "$in_xresolution" = auto ]; then
                        rm -f "$CACHED_CONF"
                    fi

					[ -n "$XORG_CONF_COMPAT" ] || commit_cache
                fi
				;;
			    driver)
                if [ -n "$in_xdriver" ]; then
                    if is_need_makeinitrd "$in_xdriver" "$(get_xdriver)"; then
                        NEED_MAKEINITRD=1
                    fi
                    if [ -n "$XORG_CONF_COMPAT" ]; then
                        video_setup "$in_xdriver" "$XORGCONFIG_WORKCOPY"
                        $XCONFTOOL -C "$(video_drv -s depth "$in_xdriver")" "$XORGCONFIG_WORKCOPY" "$XORGCONFIG_WORKCOPY"
                    else
                        # Write driver into xorg.conf.d/
                        write_xdriver "$in_xdriver"

						# Check current depth in config
						# for new driver, reset to auto if needed.
                        check_xdepth "$in_xdriver" "$(get_xdepth)" || write_depth auto
                    fi
				fi
				;;
			esac
			;;
	esac
}

message_loop
