#!/bin/bash -e

## Copyright (C) 2020 - 2026 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## provides was_executed
# shellcheck source=../libexec/helper-scripts/check_runtime.bsh
source "${HELPER_SCRIPTS_PATH:-}"/usr/libexec/helper-scripts/check_runtime.bsh

## provides has
# shellcheck source=../libexec/helper-scripts/has.bsh
source "${HELPER_SCRIPTS_PATH:-}"/usr/libexec/helper-scripts/has.bsh

## True if a DRM node is present.
gpu_node_present() {
   local dri_dir node

   dri_dir="${DETECT_SOFTWARE_RENDERING_DRI_DIR:-/dev/dri}"
   for node in "${dri_dir}"/renderD* "${dri_dir}"/card* ; do
      if [ -e "${node}" ]; then
         return 0
      fi
   done

   ## NOTE: No check for /dev/nvidiactl by design. Graphics rendering is
   ## provided through DRI devices as scanned for above. It is possible for
   ## /dev/nvidiactl or other non-DRI GPU-related devices to exist and hardware
   ## rendering still be unavailable.
   return 1
}

## Classify ONE 'OpenGL core profile renderer:' line: prints software,
## accelerated or unknown. A software marker wins over a vendor substring
## within the line.
classify_renderer_line() {
   local line="$1"

   if grep --ignore-case --fixed-strings \
      -e "llvmpipe" \
      -e "softpipe" \
      -e "swrast" \
      -e "software renderer" \
      -e "software rasterizer" \
      -e "basic render driver" \
      <<< "${line}" >/dev/null 2>&1; then
      printf '%s\n' "software"
      return 0
   fi

   if grep --fixed-strings --word-regexp \
      -e "AMD" \
      -e "NVIDIA" \
      -e "Intel" \
      -e "Apple" \
      -e "Adreno" \
      -e "Radeon" \
      -e "ATI" \
      -e "Mali" \
      -e "Panfrost" \
      -e "V3D" \
      -e "VC4" \
      -e "PowerVR" \
      -e "Vivante" \
      -e "etnaviv" \
      -e "Lima" \
      -e "virgl" \
      -e "SVGA3D" \
      -e "D3D12" \
      <<< "${line}" >/dev/null 2>&1; then
      printf '%s\n' "accelerated"
      return 0
   fi

   printf '%s\n' "unknown"
}

## Classify the OpenGL renderer. Best-effort only. Prints exactly one word:
## accelerated, software or unknown.
probe_renderer() {
   local eglinfo_output renderer_lines renderer_line timeout_maybe saw_hardware
   local saw_software libgl_always_software

   ## LIBGL_ALWAYS_SOFTWARE is a Mesa3D variable to force software rendering.
   ## See https://docs.mesa3d.org/envvars.html.
   libgl_always_software="${LIBGL_ALWAYS_SOFTWARE:-}"
   case "${libgl_always_software,,}" in
      1|true|yes|y|t|on)
         printf '%s\n' "software"
         return 0
         ;;
   esac

   ## No GPU node -> no hardware driver -> software.
   if ! gpu_node_present ; then
      printf '%s\n' "software"
      return 0
   fi

   ## A GPU node is present but eglinfo is not installed -> cannot tell -> unknown.
   if ! has eglinfo ; then
      printf '%s\n' "unknown"
      return 0
   fi

   eglinfo_output=""
   renderer_line=""
   timeout_maybe=""

   if has timeout ; then
      timeout_maybe="timeout --kill-after=5 5"
   fi
   # shellcheck disable=SC2086
   eglinfo_output="$(${timeout_maybe} env --unset=DISPLAY --unset=WAYLAND_DISPLAY --unset=WAYLAND_SOCKET eglinfo -B 2>/dev/null)" || true
   renderer_lines="$(printf '%s\n' "${eglinfo_output}" | grep --fixed-strings -- "OpenGL core profile renderer:")" || true

   ## 'eglinfo -B' prints a renderer line per EGL platform. A platform that
   ## cannot initialise can fall back to llvmpipe while another reports the
   ## real GPU. Classify each line, let hardware win across platforms. Only
   ## when no line is hardware and at least one is software do we report
   ## software.
   saw_hardware="false"
   saw_software="false"
   while IFS= read -r renderer_line ; do
      [ -n "${renderer_line}" ] || continue
      case "$(classify_renderer_line "${renderer_line}")" in
         accelerated)
            saw_hardware="true"
            ;;
         software)
            saw_software="true"
            ;;
      esac
   done <<< "${renderer_lines}"

   if [ "${saw_hardware}" = "true" ]; then
      printf '%s\n' "accelerated"
      return 0
   fi
   if [ "${saw_software}" = "true" ]; then
      printf '%s\n' "software"
      return 0
   fi

   printf '%s\n' "unknown"
}

## Best-effort guess of the renderer type. Prints one of, and returns:
##   software      0   llvmpipe (mesa CPU renderer)
##   accelerated   1   a known hardware vendor renderer
##   unknown       2   eglinfo absent / empty / timed out / unrecognized
detect_software_rendering() {
   local result

   result="$(probe_renderer)"
   printf '%s\n' "${result}"
   case "${result}" in
      software)
         return 0
         ;;
      accelerated)
         return 1
         ;;
      *)
         return 2
         ;;
   esac
}

main() {
   set -o errexit
   set -o nounset
   set -o pipefail
   set -o errtrace
   shopt -s inherit_errexit
   shopt -s shift_verbose
   export LC_ALL=C

   detect_software_rendering "$@"
}

if was_executed "${BASH_SOURCE[0]}"; then
   main "$@"
fi
