#!/bin/sh
#
# Copyright (c) 2020, The ULBSD Project
# Copyright (c) 2020, The NomadBSD Project
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# $Id: initgfx 53 2021-01-23 12:57:13Z sv $
#
# PROVIDE: initgfx
# REQUIRE: FILESYSTEMS NETWORKING
# BEFORE: sddm

. /etc/rc.subr

name=initgfx
start_cmd="do_initgfx"

rcvar=initgfx_enable

load_rc_config $name

: ${initgfx_enable:="NO"}
: ${initgfx_menu:="NO"}

path_xorg_cfg="/usr/local/etc/X11/xorg.conf.d/10-video-${name}.conf"
path_nvidia="/usr/local/share/ulbsd/drivers/nvidia"
nvidia_drivers="440 390 340 304"

initgfx_autodetect=0
initgfx_use_vesa=0
initgfx_use_scfb=0

path_initgfx_device_db="/usr/local/etc/${name}/device.db"
path_initgfx_xorg_templates="/usr/local/etc/${name}/xorg.cfg"

# Include device info DB
. ${path_initgfx_device_db}

# Include Xorg config file templates
. ${path_initgfx_xorg_templates}

gfx_menu()
{
	local ret="/tmp/gfx_menu.ret"
	local lang driver msg hline opts efiboot
	if sysctl machdep.bootmethod | grep -q BIOS; then
		efiboot=0
		driver="VESA"
	else
		efiboot=1
		driver="SCFB"
	fi

	if [ "${sddm_lang}" == "ru_RU" ]
	then
	    lang="ru_RU"
	else
	    lang="en_US"
	fi

	if [ "${lang}" == "ru_RU" ]
	then
		title="Драйвер графического режима"
		msg="Если при автоматическом определении драйвера графического"
		msg="${msg} режима возникают проблемы, то вы можете выбрать драйвер"
		msg="${msg} ${driver}. Драйвер ${driver} не поддерживает аппаратное ускорение"
		msg="${msg} графики, но работает на большинстве систем."
		hline="Используйте ↑↓ и ENTER для выбора"
		opts="Autodetect:Автоматическое определение драйвера"
		if [ ${efiboot} -eq 1 ]; then
			opts="${opts}:SCFB:Универсальный драйвер"
		else
			opts="${opts}:VESA:Универсальный драйвер"
		fi
	else
		title="Graphics mode driver"
		msg="If you have problems with the automatic detection of the"
		msg="${msg} graphics driver, you can select the ${driver} driver."
		msg="${msg} The ${driver} driver does not support hardware graphics"
		msg="${msg} acceleration, but it works on most systems."
		hline="Use ↑↓ and ENTER for choice"
		opts="Autodetect:Autodetect graphics driver"
		if [ ${efiboot} -eq 1 ]; then
			opts="${opts}:SCFB:Generic video driver"
		else
			opts="${opts}:VESA:Generic video driver"
		fi
	fi

	while [ true ]; do
		local IFS=:
                TERM=xterm env LANG="${lang}.UTF-8" /usr/local/bin/cdialog --title "${title}" --hline "${hline}" --no-collapse --no-cancel --menu "${msg}" 12 62 2 ${opts} 2>${ret}
		[ $? -ne 0 ] && continue
		opt=$(cat ${ret})
		rm -f ${ret}
		case ${opt} in
			Autodetect)
				initgfx_autodetect=1
				;;
			SCFB)
				initgfx_use_scfb=1
				;;
			VESA)
				initgfx_use_vesa=1
				;;
		esac
		return
	done
}

get_device_info() {
	local chip
	unit=$(sysctl -n hw.pci.default_vgapci_unit)
	[ -z "$unit" -o "$unit" =  "-1" ] && unit=0
	chip=$(pciconf -lv | grep ^vgapci${unit} | head -1 | \
		sed -E 's#.* chip=0x([0-9a-z]{4})([0-9a-z]{4}).*$#\1:\2#')
	busID=$(pciconf -lv | grep ^vgapci${unit} | head -1 | \
		sed -E 's/^vgapci[0-9]@pci[0-9]:([0-9:]+):.*$/\1/')
	vendorID=$(echo "${chip}" | cut -d: -f2)
	deviceID=$(echo "${chip}" | cut -d: -f1)
	deviceStr=$(pciconf -lv | grep -A3 ^vgapci${unit} | grep device | \
		cut -d"'" -f 2)
}

setup_ati_amd()
{
	[ "$vendorID" != "1002" ] && return 1

	if radeon_ids | grep -iq ${deviceID}; then
		kldload -n /boot/modules/radeonkms.ko
		ati_xorg_cfg > "${path_xorg_cfg}"
		sysrc kld_list+=/boot/modules/radeonkms.ko
	else
		kldload -n /boot/modules/amdgpu.ko
		amd_xorg_cfg > "${path_xorg_cfg}"
		sysrc kld_list+=/boot/modules/amdgpu.ko
	fi
	return 0
}

setup_intel()
{
	[ "$vendorID" != "8086" ] && return 1
	kldload -n /boot/modules/i915kms.ko
	modesetting_xorg_cfg > "${path_xorg_cfg}"
	sysrc kld_list+=/boot/modules/i915kms.ko
	return 0
}

setup_nvidia()
{
	local d driver

	[ "${vendorID}" != "10de" ] && return 1

	driver=""
	for d in ${nvidia_drivers}; do
		nvidia_${d}_ids | awk -v deviceStr="$deviceStr" '
			match(tolower(deviceStr), tolower($0)) > 0 {
				printf("%s ~ %s\n", deviceStr, $0);
				exit(100)
			}
		'
		if [ $? -eq 100 ]; then
			driver=$d
			break
		fi
	done

	[ -z "${driver}" ] && return 1

	[ ! -d /usr/local/etc/libmap.d ] && mkdir -p /usr/local/etc/libmap.d
	cp -f ${path_nvidia}/${driver}/usr/local/etc/libmap.d/nvidia.conf /usr/local/etc/libmap.d

	[ ! -d /usr/local/libdata/ldconfig ] && mkdir -p /usr/local/libdata/ldconfig
	echo "${path_nvidia}/${driver}/usr/local/lib" > /usr/local/libdata/ldconfig/nvidia-driver
	ldconfig -m ${path_nvidia}/${driver}/usr/local/lib

	[ ! -d /usr/local/lib/vdpau ] && mkdir -p /usr/local/lib/vdpau
	for l in $(ls ${path_nvidia}/${driver}/usr/local/lib/vdpau/*); do
		ln -sf $l /usr/local/lib/vdpau
	done

	kldload -n "${path_nvidia}/${driver}/boot/modules/nvidia.ko"
	nvidia_xorg_cfg > "${path_xorg_cfg}"
	sysrc kld_list+="${path_nvidia}/${driver}/boot/modules/nvidia.ko"
	if [ ${driver} -ge 390 ]; then
		kldload -n "${path_nvidia}/${driver}/boot/modules/nvidia-modeset.ko"
		sysrc kld_list+="${path_nvidia}/${driver}/boot/modules/nvidia-modeset.ko"
	fi
	return 0
}

setup_vmware()
{
	pciconf -lv | grep -B3 display | grep -q -i vmware || return 1
	vmware_xorg_cfg > "${path_xorg_cfg}"
	return 0
}

setup_virtualbox()
{
	pciconf -lv | grep -B3 display | grep -q -i virtualbox || return 1
	virtualbox_xorg_cfg > "${path_xorg_cfg}"
	return 0
}

do_initgfx()
{
	local i l driver
	
	# Do not show menu if syscons is disabled or initgfx_menu="NO"
	syscons_disabled=$(kenv | awk -F '["=]+' '
		/hw.syscons.disable/ { print $2 }')
	if [ ${syscons_disabled:=0} -eq 0 ]; then
		sysons_disabled=$(sysctl -n hw.syscons.disable 2>/dev/null)
	fi
	if checkyesno initgfx_menu; then
		if [ ${syscons_disabled:=0} -eq 0 ]; then
			gfx_menu
		else
			initgfx_autodetect=1
		fi
	elif [ -n "${initgfx_default}" ]; then
		case ${initgfx_default} in
		vesa|scfb)
			initgfx_autodetect=0
			;;
		*)
			echo "Error: Unknown driver ${initgfx_default}. " \
				 "Use scfb or vesa" >&2
			initgfx_autodetect=1
			;;
		esac
	else
		initgfx_autodetect=1
	fi

	# Removes previous graphics mode settings
	rm -f ${path_xorg_cfg}
	sysrc kld_list-=/boot/modules/radeonkms.ko >/dev/null
	sysrc kld_list-=/boot/modules/amdgpu.ko >/dev/null
	sysrc kld_list-=/boot/modules/i915kms.ko >/dev/null
	for i in ${nvidia_drivers}; do
		sysrc kld_list-="${path_nvidia}/${i}/boot/modules/nvidia.ko" >/dev/null
		sysrc kld_list-="${path_nvidia}/${i}/boot/modules/nvidia-modeset.ko" >/dev/null
		for l in $(ls ${path_nvidia}/${i}/usr/local/lib/vdpau/); do
			rm -f /usr/local/lib/vdpau/$l
		done
	done
	rm -f /usr/local/etc/libmap.d/nvidia.conf
	rm -f /usr/local/libdata/ldconfig/nvidia-driver

	get_device_info

	if [ ${initgfx_autodetect} -eq 1 ]; then
		for i in nvidia intel ati_amd vmware virtualbox; do
			setup_${i} && sync && return
		done
	fi

	driver=""
	if [ ${initgfx_use_vesa} -eq 1 ]; then
		driver="vesa"
	elif [ ${initgfx_use_scfb} -eq 1 ]; then
		driver="scfb"
	elif [ -n "${initgfx_default}" ]; then
		driver=${initgfx_default}
	else
		echo "Error: No graphics driver found. " \
			"Try using scfb or vesa on next reboot." >&2
		return
	fi

	(printf "Section \"Device\"\n"; \
	 printf "\tIdentifier \"Card0\"\n"; \
	 printf "\tDriver \"${driver}\"\n"; \
	 printf "\tBusID \"PCI:$busID\"\n"; \
	 printf "EndSection\n") > ${path_xorg_cfg}
}

run_rc_command "$1"
