#!/bin/bash
#
#
###########################################################
# GPL 
# (c) 2002,2003 SuSE Linux AG
#     2004-2011 dass IT GmbH
#
# Authors 
#    Fabian Herschel
#    Holger Mueller
#    Joerg Steffens, dass IT
#    Sebastian Lederer, CONET AG, dass IT
#
# $Id: sc_hardware_X 1091 2011-11-30 15:03:07Z joergs $
###########################################################

### BEGIN INIT INFO
# Provides:       sc_hardware_X
# Required-Start: $local_fs $network sc_mkwsconfig
# Should-Start:   $remote_fs hwscan sc_hardware_lid sc_hardware_prepare
# Required-Stop:  $local_fs
# Should-Stop:    $null
# Default-Start:  5
# Default-Stop:
# Description:    configure the X11 hardware
### END INIT INFO


. /etc/rc.status
rc_reset


# include SmartClient hardware helper functions
test -r /usr/lib/smartclient/sc_hardware.sh && source /usr/lib/smartclient/sc_hardware.sh
sc_hardware_init

default_depth=${default_depth:-24}
getattr=/usr/bin/sc_getattr.pl
getattr_default="$getattr"
getattr_nodefault="$getattr --nodefault"
getmonitor=/usr/sbin/sc_getmonitor.pl
# use 
# /etc/X11/xorg.conf for X.org or 
# /etc/X11/XF86Config for XF86 (deprecated)
xconfig=/etc/X11/xorg.conf


do_the_monitor_config()
{
    MODE=${1:-normal} # normal / status 

    if [ $MODE == "normal" ] ; then

        #
        # special case for nvidia (dual head)
        #
        if hwinfo --gfxcard | grep -q -i "Vendor:.*nVidia"; then

            if ! modprobe nvidia; then
                if [ "$SC_HARDWARE_X_NVIDIA_INSTALLER" ]; then
                    if [ ! -e "$SC_HARDWARE_X_NVIDIA_INSTALLER" ]; then
                        echo
                        echo -n "NVIDIA driver $SC_HARDWARE_X_NVIDIA_INSTALLER not found "
                    else
                        echo -n "installing NVIDIA graphics driver "
                        sh $SC_HARDWARE_X_NVIDIA_INSTALLER -s -N --kernel-source-path=/usr/src/linux >/dev/null 2>/dev/null
                        rc_status -v
                    fi
                fi
            fi

            # NVidia brings its own libglx.so
            # This library generates conflicts on other systems,
            # therefore the final link is 
            # not transfered by SmartClient
            # and only generated on nvidia systems
            # see https://trac.dass-it.de/lvermgeo/ticket/241
            if [ "${SC_HARDWARE_X_NVIDIA_LIBGLX}" ]; then
                if ! [ -r "${SC_HARDWARE_X_NVIDIA_LIBGLX}" ]; then
                    # get latest libglx version
                    LIBGLX_LATEST=`ls  ${SC_HARDWARE_X_NVIDIA_LIBGLX}.* | sort -r | head -n 1`
                    if [ "${LIBGLX_LATEST}" ]; then
                        ln -s ${LIBGLX_LATEST} ${SC_HARDWARE_X_NVIDIA_LIBGLX}
                    fi
                fi
            fi

            echo -n "loading NVIDIA kernel module "
            modprobe nvidia
            rc_status -v
            nvidia_dual_head=YES
        fi

        #
        # special case for i810
        #
        if hwinfo --gfxcard | grep -q 'Device:.*pci.*0x1132'
        then
                default_depth=16
        fi
    fi

    #
    # get the needed HW-Info for the monitor
    # (only one could be handled at the moment)
    # ' ' and '+' are replaced by '_'
    # '"' is replaced by ''
    #

    logger -p local0.info -t sc_hardware_X "HW_MONITOR_CONFIG STARTED"
    THE_HW_INFO=$(hwinfo --monitor | awk -F: '
    BEGIN { printed=0; found=0; };
        $1 ~ /Model/        { found=1; MODEL = $2; 
            gsub(" |+", "_", MODEL);
            gsub("\"", "", MODEL) };
        $1 ~ /Vendor/       { found=1; VENDOR = $2; 
            gsub(" |+", "_", VENDOR);
            gsub("\"", "", VENDOR) };
        $1 ~ /Size/     { found=1; SIZE = $2;
            gsub("cm", "", SIZE);
            gsub(" ", "", SIZE) };
        $1 ~ /Driver Info #0/   {
            printed=1;
            printf "MONTYPE=MONITOR_%s_%s;MONSIZE=MONITOR_SIZE_%s",
            VENDOR, MODEL, SIZE };
    END { 
        if ( (printed != 1) && (found) ) { 
            printf "MONTYPE=MONITOR_%s_%s;MONSIZE=MONITOR_SIZE_%s",
                VENDOR, MODEL, SIZE }
        };
    ')

	#
	# For non detectable monitors use a standard string
	#
	if [ -z "$THE_HW_INFO" ]; then
		THE_HW_INFO="MONTYPE=StdNoDetect"
	else
		getattr="$getattr_nodefault"
		#
		# if hwinfo fails, use LDAP default values for monitor
		#
	fi

    eval $THE_HW_INFO
    RES=""
	VR=""
	HR=""

    #
    # Rechnerspezifische Auflösungseinstellung
    #
    RES="$($getattr_nodefault scMonitor1Resolutions)"

    #
    # Monitortyp im LDAP suchen
    #
    if [ "$MONTYPE" = StdNoDetect ]; then
        echo "No monitor detected, using settings from LDAP"
    else
        echo -n "Searching for monitor type $MONTYPE in LDAP..."
        eval set "$($getmonitor $MONTYPE)" >/dev/null
        if [ -n "$1" ]; then
            #
            # if found, use monitor specs from LDAP
            #
            echo "found."
            if [ -z "$RES" ]; then
                RES="$1"
            fi
            HR="$2"	
            VR="$3"
        else
            echo "not found."
        fi
    fi 
    #echo "MONTYPE=$MONTYPE"
    #echo "MONSIZE=$MONSIZE"
    
    if [ -z "$RES" ]; then
    	RES="$($getattr scRefMonitor1Dn dbkMonitorRes)"
    fi

    if [ -z "$HR" ]; then
    	HR="$($getattr scRefMonitor1Dn dbkHsync)"
    fi
    
    if [ -z "$VR" ]; then
    	VR="$($getattr scRefMonitor1Dn dbkVsync)"
    fi

    # immer noch keine Aufloesung, dann Default-Wert nehmen 
    if [ -z "$RES" ]; then
        RES="$($getattr scMonitor1Resolutions)"
    fi 

    if [ $MODE == "status" ]; then
        return 0
    fi

    sax2_batch=""

    sax2_batch_file="`mktemp /tmp/sax2_batch_file-XXXXXX`"
    sax2_batch="$sax2_batch_file"

    if [ -z "$RES" ] ; then
        logger -p local0.warn -t sc_hardware_X "Using DDC resolutions"
        echo "Using DDC resolutions"
        echo "Screen->[X]->DefaultDepth=$default_depth\"" >> $sax2_batch_file
    else
        echo "Using resolutions $RES hsync $HR KHz vrefresh $VR Hz";echo
        logger -p local0.info -t sc_hardware_X "MONTYPE=$MONTYPE; MONSIZE=$MONSIZE; -> RESOLUTION=$RES"
        #
        # TODO: Do we need additional depths?
        #
        # echo RES $RES
        saxRES=$(echo $RES | tr ' ' ',')
        echo "Screen->[X]->Depth->8->Modes=\"$saxRES\"" >> $sax2_batch_file
        echo "Screen->[X]->Depth->16->Modes=\"$saxRES\"" >> $sax2_batch_file
        echo "Screen->[X]->Depth->24->Modes=\"$saxRES\"" >> $sax2_batch_file
        echo "Screen->[X]->Depth->32->Modes=\"$saxRES\"" >> $sax2_batch_file
        echo "Screen->[X]->DefaultDepth=$default_depth\"" >> $sax2_batch_file
        echo "Monitor->[X]->ModelName=$MONTYPE" >>$sax2_batch_file
        #echo "Monitor->[X]->Modeline=" >>$sax2_batch_file
        if [ -n "$VR" ]; then
            echo "Monitor->[X]->VertRefresh=$VR" >> $sax2_batch_file 
        fi
        if [ -n "$HR" ]; then
            echo "Monitor->[X]->HorizSync=$HR" >> $sax2_batch_file     
        fi
    fi 

    if [ "$nvidia_dual_head" ]; then
        if [ "$SC_HARDWARE_X_NVIDIA_DUALHEAD_HACK" = "yes" ]; then
            echo "Device->[X]->Driver=nvidia" >> $sax2_batch_file
            echo "Device->[X]->Screen=0" >> $sax2_batch_file
            echo "Device->[X]->Chipset=" >> $sax2_batch_file
            echo "Device->[X]->Option=" >> $sax2_batch_file
            echo "Device->[1]->Driver=nvidia" >> $sax2_batch_file
            echo "Device->[1]->Identifier=Device[1]" >>$sax2_batch_file
            echo "Device->[1]->Screen=1" >>$sax2_batch_file
            echo "Device->[1]->BusID=BUSID_DUMMY" >>$sax2_batch_file
        else
            # add dual head options
            #sax2_batch = "$sax2_batch -b Xinerama"

            # sax profile Xinerama does not work.
            # sax profile MergedFB does work, but produces a cloned screen view.
            # we replace the option "TwinViewOrientation" "Clone"
            # by "TwinViewOrientation" "RightOf"
            # to enable Xinerama-like screens
            # first line is what we want, but does not work.
            # second line is taken from 
            # /usr/share/sax/profile/NVidia_DualHead_DriverOptions
            # and adapted to our needs
            echo 'Device->[X]->Option->TwinViewOrientation=RightOf' >> $sax2_batch_file
            echo 'Device->[X]->Raw->11->Option = "TwinViewOrientation" "RightOf"' >> $sax2_batch_file 
            sax2_batch="MergedFB,$sax2_batch"
        fi
    fi

    #
    # now start the sax2 
    # with an optional batch for changing the resolution and vert refresh
    #
    yes "" | $SC_HARDWARE_X_SAX -r -a -b $sax2_batch || return 1

    getattr="$getattr_default"

    if [ "$SC_HARDWARE_X_NVIDIA_DUALHEAD_HACK" = "yes" ]; then
        if is_dual_head; then
            # fix dual head configuration
            #
            # FIXME: get monitor type from LDAP
            #
            dual_head_monitor=$($getattr scDualHeadMonitor)
            do_second_head_config $dual_head_monitor
        else
            #
            # disable dual head configuration
            #
            perl -i -ne "s/(^\s*Screen\s.*Screen.*Screen.*)/\#\1/;print" $xconfig
            perl -i -ne "s/(^\s*Screen\s.*Screen)\[1\](.*)/\1\[0\]\2/;print" $xconfig
        fi
    fi

    echo "$sax2_batch" > /var/tmp/sax2_batch_options
    test -e $sax2_batch_file && mv $sax2_batch_file /var/tmp/sax2_batch_file
    logger -p local0.info -t sc_hardware_X "HW_MONITOR_CONFIG FINISHED"
}


is_dual_head()
{
	#
	# special case for nvidia cards
	# which are not detected correctly by sax
	#

	if [ -n "$nvidia_dual_head" ]; then
		return 0 # true
	fi

	# only make dual-head-configuration if there is
	# a second screen section
	if ! grep -q 'Identifier.*Screen.1.' $xconfig; then
		return 1 # false
	fi

	second_monitor=$($getattr scRefMonitor2Dn cn)
	if [ -n "$second_monitor" ]; then
		return 0 # true
	fi

	return 1 # false
}

replace_2nd_section()
{
	section=$1
	new=$2

	ed $xconfig >/dev/null 2>&1 <<EOF
#
H
/Section "$section"/
/Section "$section"/
.,/EndSection/d
.r $new
w
q
EOF
}


rewrite_server_layout()
{
	tmp=$(mktemp)
	$getattr scRefScreenLayoutDn scScreenLayoutOptions >$tmp
	if [ $? != 0 ]
	then
		cat >$tmp <<EOF
		Screen "Screen[0]"
		Screen "Screen[1]" RightOf "Screen[0]"
		Option "Xinerama" "On"
EOF
	fi
	
	sed -i -e '/Section .ServerLayout./,/EndSection/ { s/Option.*//;s/Screen.*//}' -e "/Section .ServerLayout./ r $tmp" $xconfig
	rm $tmp
}

add_tablet_to_server_layout()
{
        tmp=$(mktemp)
        cat >$tmp <<EOF
  InputDevice  "tablet" "SendCoreEvents"
  InputDevice  "stylus" "SendCoreEvents"
EOF
        sed -i -e "/InputDevice.*\"Mouse\[1\]\"/ r $tmp" $xconfig
        rm $tmp
}

add_tablet_input_devices()
{
    # default device (Panasonic)
    dev=/dev/ttyS4
    if isMettenmeierColibriX6; then
        dev=/dev/ttyS1
    fi
    
    tmp=$(mktemp)
    cat >$tmp <<EOF

Section "InputDevice"
  Driver        "wacom"
  Identifier    "tablet"
  Option        "Mode" "Absolute"
  Option        "Type" "cursor"
  Option        "Device" "$dev"
  Option        "Name"  "Wacom Digitizer Screen"
  Option        "SendCoreEvents" "on"
  Option        "ForceDevice" "ISDV4"
#  Option        "DebugLevel" "12"
  Option        "KeepShape" "on"
EndSection

Section "InputDevice"
  Driver        "wacom"
  Identifier    "stylus"
  Option        "Mode" "Absolute"
  Option        "Type" "stylus"
  Option        "Device" "$dev"
  Option        "Name" "Wacom Digitizer Screen"
  Option        "SendCoreEvents" "on"
  Option        "ForceDevice" "ISDV4"
#  Option        "DebugLevel" "12"
EndSection

EOF

        # use ed to insert above text after the first InputDevice section

        ed $xconfig >/dev/null 2>&1 <<EOF
#
H
/Section "InputDevice"/
/EndSection/

.r $tmp
w
q
EOF

        rm $tmp
}

#
# TabletPC special case
#
do_tabletpc_config()
{
    # TODO: remove, because not used ??
    echo -n "using tablet pc configuration "
    add_tablet_input_devices
    add_tablet_to_server_layout
}


#
# For the laptop special case,
# insert parameters for external monitor, using the LDAP
# configuration for the primary monitor
#
do_laptop_config()
{
    RES=""
    HR=""
    VR=""
    MON="$($getattr scRefMonitor1Dn)" 

    echo "  using laptop graphics configuration."

    if [ -z "$MON" ]; then
        echo
        return 0
    fi

    if [ -z "$RES" ]; then
        RES="$($getattr scRefMonitor1Dn dbkMonitorRes)"
    fi

    if [ -z "$HR" ]; then
        HR="$($getattr scRefMonitor1Dn dbkHsync)"
    fi

    if [ -z "$VR" ]; then
        VR="$($getattr scRefMonitor1Dn dbkVsync)"
    fi

    if [ -z "$RES" ]; then
        RES="$($getattr scMonitor1Resolutions)"
    fi

    echo "  external monitor: $MON ( $RES )"

    sed -i '/^Section "Monitor"/,/^EndSection/ d' /etc/X11/xorg.conf.extonly
    cat >>/etc/X11/xorg.conf.extonly <<EOF
Section "Monitor"
# Monitor section for external monitor
  Option       "CalcAlgorithm" "XServerPool"
  Identifier   "Monitor[0]"
  ModelName    "$MON"
  Option       "DPMS"
  UseModes     "Modes[0]"
  HorizSync     $HR
  VertRefresh   $VR
EndSection
EOF

}

#
# because sax does not recognize the second monitor,
# we have to configure it manually 
# 
do_second_head_config()
{
	device="Device[1]"
	screen="Screen[1]"
	monitor="Monitor[1]"
	modes='"1024x768"'
	hsync="32-64"
	vrefresh="70-90"

	tmp=/tmp/sc_hardware_X.tmp

	monitor_type=$($getattr scRefMonitor2Dn cn)
	modes="$($getattr scMonitor2Resolutions)"
	if [ -z "$modes" ]
	then
		modes=$($getattr scRefMonitor2Dn dbkMonitorRes)
	fi
	hsync=$($getattr scRefMonitor2Dn dbkHsync)
	vrefresh=$($getattr scRefMonitor2Dn dbkVsync)	

	echo "Using monitor type "$monitor_type" for secondary head:"
	echo "mode: $modes vrefresh: $vrefresh hsync: $hsync"


	if [ -n "$nvidia_dual_head" ]
	then
		# create missing sections
		cat >>$xconfig <<EOF
		Section "Monitor"
			Identifier "Monitor[1]"
		EndSection

		Section "Screen"
			Identifier "Screen[1]"
		EndSection

		Section "Modes"
			Identifier "Modes[1]"
		EndSection
EOF

		# fix second Device section
		busid=$(awk '/BusID.*[0-9]/ { print $2 }' $xconfig)
		eval busid=$busid  # remove apostrophes
		sed -i "s/BUSID_DUMMY/$busid/" $xconfig
	
	fi


	#
	# make a new monitor section
	#
	#################################
	cat >$tmp <<EOF
Section "Monitor"
	# hacked second head section
	Identifier "$monitor"
	HorizSync $hsync
	VertRefresh $vrefresh
	Option "DPMS"
	UseModes "Modes[1]"
	# "$monitor_type"
EndSection
EOF
	replace_2nd_section "Monitor" $tmp


	# make a new modes section
	###################################
	cat >$tmp <<EOF
Section "Modes"
	# hacked second head section
	Identifier "Modes[1]"
    #  Modeline 	"1024x768" 67.48 1024 1080 1184 1344 768 769 772 797
    #  Modeline 	"1024x768" 79.52 1024 1080 1192 1360 768 769 772 801
    #  Modeline 	"1024x768" 92.05 1024 1088 1200 1376 768 769 772 806
EndSection
EOF
	replace_2nd_section "Modes" $tmp


	# make a new screen section
	#####################################
	cat >$tmp <<EOF
Section "Screen"
	# hacked second head section
	DefaultDepth $default_depth
	Device "$device"
	Identifier "$screen"
	Monitor "$monitor"
	SubSection "Display"
		Depth 32
		Modes $modes
	EndSubSection
	SubSection "Display"
		Depth 24
		Modes $modes
	EndSubSection
	SubSection "Display"
		Depth 16
		Modes $modes
	EndSubSection
	SubSection "Display"
		Depth 8
		Modes $modes
	EndSubSection
EndSection
EOF
	replace_2nd_section "Screen" $tmp
	
	rewrite_server_layout
}


do_the_mouse_config()
{
    MODE=${1:-normal} # normal / status 
    
    #
    # Get the needed HW-Info for the mouse
    # (only one could be handled at the moment)
    #
    logger -p local0.info -t sc_hardware_X "HW_MOUSE_CONFIG STARTED"
    THE_MOUSE=$(hwinfo --mouse | awk -F: '
	BEGIN { printed=0; found=0; };
	$1 ~ /Device File$/	{ found=1;
		 DEVICE = $2;printf "%s", DEVICE;};
    ' | sed 's/(.*)//g' )
    if [ -z "$THE_MOUSE" ]; then
        echo "No Mice found!"
    else 
        echo "Mouse at device: $THE_MOUSE"
        if [ "$MODE" != "status" ]; then
            rm -f /dev/mouse
            ln -s $THE_MOUSE /dev/mouse
        fi
    fi
    logger -p local0.info -t sc_hardware_X "HW_MOUSE_CONFIG FINISHED: using $THE_MOUSE"
}



case $1 in
    start|restart|force-start)
        echo "SmartClient X configuration "

        if [   $1 = "force-start" \
            -o "$SC_HARDWARE_X_START" = "ONBOOT" \
            -o ! -e "$SC_HARDWARE_X_MARKER"  ]; then
            #echo

            if is_pcType_specific_config $xconfig; then
                # is should already by done by /etc/init.d/sc_hardware_prepare,
                # but maybe, this is not activated, so to be safe, it is done again
                do_pcType_specific_config $xconfig
            elif isLaptopPC; then
                do_laptop_config
            else
                do_the_monitor_config
                do_the_mouse_config
            fi

            #
            # set the "semaphore" file
            #
            echo "delete me, if you want to configure X again" > $SC_HARDWARE_X_MARKER
            chmod 666 $SC_HARDWARE_X_MARKER

            rc_status -v
        else
            echo -n "  $SC_HARDWARE_X_MARKER exists "
            rc_status -s
        fi
		;;
    status) 
		do_the_monitor_config "status"
		do_the_mouse_config "status"
		;;
    stop) 
        exit 0
		;;
    *) 
		echo "Usage: $0 {start|stop|status|force-start}"
		exit 1
		;;
esac

rc_exit
