#!/bin/sh
#
# xconfig - Intelligent X11 configuration tool for GhostBSD / FreeBSD
#
# - Detects GPUs and common hypervisors
# - Installs appropriate drivers (optionally from /xdrivers)
# - Applies tuned Xorg configuration templates
# - Supports Xorg and XLibre X servers
#
# This version includes explicit bhyve detection which maps to the scfb template.
#

set -eu

SCRIPT_NAME=$(basename "$0")
SCRIPT_DIR=$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")")

LOG_FILE="/var/log/xconfig.log"
XORG_CONF="/etc/X11/xorg.conf"
BACKUP_DIR="/etc/X11/backup"

# Where to look for cardDetect templates
LOCAL_CONFIG_DIR="$SCRIPT_DIR/cardDetect"
SYSTEM_CONFIG_DIR="/usr/local/etc/X11/cardDetect"

# Check if we have pkg-static and use it instead:
if which -s "pkg-static"
then
    PKG="pkg-static"
else
    PKG="pkg"
fi

# List of packages to remove:
TO_REMOVE=""
# List of packages to remove when invoked with --clean:
TO_CLEAN_REMOVE=""
TO_CLEAN="false"

TO_REMOVE_XDRIVERS="false"

# Default to POSIX locale for predictable dialog output
export LC_ALL=C

# Radeon device IDs that require radeonkms instead of amdgpu
# Source: github.com/freebsd/drm-kmod (6.6-lts) include/drm/drm_pciids.h (radeon_PCI_IDS)
# Plus 0x4243, 0x4C6E from /usr/src/sys/dev/drm2/drm_pciids.h
# Covers: R100 through Sea Islands (Kabini/Kaveri/Mullins/Hawaii) - 701 device IDs
RADEON_DEVICE="0x1304|0x1305|0x1306|0x1307|0x1309|0x130A|0x130B|0x130C|0x130D|0x130E|0x130F|0x1310|0x1311|0x1312|0x1313|0x1315|0x1316|0x1317|0x1318|0x131B|0x131C|0x131D|0x3150|0x3151|0x3152|0x3154|0x3155|0x3E50|0x3E54|0x4136|0x4137|0x4144|0x4145|0x4146|0x4147|0x4148|0x4149|0x414A|0x414B|0x4150|0x4151|0x4152|0x4153|0x4154|0x4155|0x4156|0x4237|0x4242|0x4243|0x4336|0x4337|0x4437|0x4966|0x4967|0x4A48|0x4A49|0x4A4A|0x4A4B|0x4A4C|0x4A4D|0x4A4E|0x4A4F|0x4A50|0x4A54|0x4B48|0x4B49|0x4B4A|0x4B4B|0x4B4C|0x4C57|0x4C58|0x4C59|0x4C5A|0x4C64|0x4C66|0x4C67|0x4C6E|0x4E44|0x4E45|0x4E46|0x4E47|0x4E48|0x4E49|0x4E4A|0x4E4B|0x4E50|0x4E51|0x4E52|0x4E53|0x4E54|0x4E56|0x5144|0x5145|0x5146|0x5147|0x5148|0x514C|0x514D|0x5157|0x5158|0x5159|0x515A|0x515E|0x5460|0x5462|0x5464|0x5548|0x5549|0x554A|0x554B|0x554C|0x554D|0x554E|0x554F|0x5550|0x5551|0x5552|0x5554|0x564A|0x564B|0x564F|0x5652|0x5653|0x5657|0x5834|0x5835|0x5954|0x5955|0x5960|0x5961|0x5962|0x5964|0x5965|0x5969|0x5974|0x5975|0x5A41|0x5A42|0x5A61|0x5A62|0x5B60|0x5B62|0x5B63|0x5B64|0x5B65|0x5C61|0x5C63|0x5D48|0x5D49|0x5D4A|0x5D4C|0x5D4D|0x5D4E|0x5D4F|0x5D50|0x5D52|0x5D57|0x5E48|0x5E4A|0x5E4B|0x5E4C|0x5E4D|0x5E4F|0x6600|0x6601|0x6602|0x6603|0x6604|0x6605|0x6606|0x6607|0x6608|0x6610|0x6611|0x6613|0x6617|0x6620|0x6621|0x6623|0x6631|0x6640|0x6641|0x6646|0x6647|0x6649|0x6650|0x6651|0x6658|0x665C|0x665D|0x665F|0x6660|0x6663|0x6664|0x6665|0x6667|0x666F|0x6700|0x6701|0x6702|0x6703|0x6704|0x6705|0x6706|0x6707|0x6708|0x6709|0x6718|0x6719|0x671C|0x671D|0x671F|0x6720|0x6721|0x6722|0x6723|0x6724|0x6725|0x6726|0x6727|0x6728|0x6729|0x6738|0x6739|0x673E|0x6740|0x6741|0x6742|0x6743|0x6744|0x6745|0x6746|0x6747|0x6748|0x6749|0x674A|0x6750|0x6751|0x6758|0x6759|0x675B|0x675D|0x675F|0x6760|0x6761|0x6762|0x6763|0x6764|0x6765|0x6766|0x6767|0x6768|0x6770|0x6771|0x6772|0x6778|0x6779|0x677B|0x6780|0x6784|0x6788|0x678A|0x6790|0x6791|0x6792|0x6798|0x6799|0x679A|0x679B|0x679E|0x679F|0x67A0|0x67A1|0x67A2|0x67A8|0x67A9|0x67AA|0x67B0|0x67B1|0x67B8|0x67B9|0x67BA|0x67BE|0x6800|0x6801|0x6802|0x6806|0x6808|0x6809|0x6810|0x6811|0x6816|0x6817|0x6818|0x6819|0x6820|0x6821|0x6822|0x6823|0x6824|0x6825|0x6826|0x6827|0x6828|0x6829|0x682A|0x682B|0x682C|0x682D|0x682F|0x6830|0x6831|0x6835|0x6837|0x6838|0x6839|0x683B|0x683D|0x683F|0x6840|0x6841|0x6842|0x6843|0x6849|0x684C|0x6850|0x6858|0x6859|0x6880|0x6888|0x6889|0x688A|0x688C|0x688D|0x6898|0x6899|0x689B|0x689C|0x689D|0x689E|0x68A0|0x68A1|0x68A8|0x68A9|0x68B0|0x68B8|0x68B9|0x68BA|0x68BE|0x68BF|0x68C0|0x68C1|0x68C7|0x68C8|0x68C9|0x68D8|0x68D9|0x68DA|0x68DE|0x68E0|0x68E1|0x68E4|0x68E5|0x68E8|0x68E9|0x68F1|0x68F2|0x68F8|0x68F9|0x68FA|0x68FE|0x7100|0x7101|0x7102|0x7103|0x7104|0x7105|0x7106|0x7108|0x7109|0x710A|0x710B|0x710C|0x710E|0x710F|0x7140|0x7141|0x7142|0x7143|0x7144|0x7145|0x7146|0x7147|0x7149|0x714A|0x714B|0x714C|0x714D|0x714E|0x714F|0x7151|0x7152|0x7153|0x715E|0x715F|0x7180|0x7181|0x7183|0x7186|0x7187|0x7188|0x718A|0x718B|0x718C|0x718D|0x718F|0x7193|0x7196|0x719B|0x719F|0x71C0|0x71C1|0x71C2|0x71C3|0x71C4|0x71C5|0x71C6|0x71C7|0x71CD|0x71CE|0x71D2|0x71D4|0x71D5|0x71D6|0x71DA|0x71DE|0x7200|0x7210|0x7211|0x7240|0x7243|0x7244|0x7245|0x7246|0x7247|0x7248|0x7249|0x724A|0x724B|0x724C|0x724D|0x724E|0x724F|0x7280|0x7281|0x7283|0x7284|0x7287|0x7288|0x7289|0x728B|0x728C|0x7290|0x7291|0x7293|0x7297|0x7834|0x7835|0x791E|0x791F|0x793F|0x7941|0x7942|0x796C|0x796D|0x796E|0x796F|0x9400|0x9401|0x9402|0x9403|0x9405|0x940A|0x940B|0x940F|0x9440|0x9441|0x9442|0x9443|0x9444|0x9446|0x944A|0x944B|0x944C|0x944E|0x9450|0x9452|0x9456|0x945A|0x945B|0x945E|0x9460|0x9462|0x946A|0x946B|0x947A|0x947B|0x9480|0x9487|0x9488|0x9489|0x948A|0x948F|0x9490|0x9491|0x9495|0x9498|0x949C|0x949E|0x949F|0x94A0|0x94A1|0x94A3|0x94B1|0x94B3|0x94B4|0x94B5|0x94B9|0x94C0|0x94C1|0x94C3|0x94C4|0x94C5|0x94C6|0x94C7|0x94C8|0x94C9|0x94CB|0x94CC|0x94CD|0x9500|0x9501|0x9504|0x9505|0x9506|0x9507|0x9508|0x9509|0x950F|0x9511|0x9515|0x9517|0x9519|0x9540|0x9541|0x9542|0x954E|0x954F|0x9552|0x9553|0x9555|0x9557|0x955F|0x9580|0x9581|0x9583|0x9586|0x9587|0x9588|0x9589|0x958A|0x958B|0x958C|0x958D|0x958E|0x958F|0x9590|0x9591|0x9593|0x9595|0x9596|0x9597|0x9598|0x9599|0x959B|0x95C0|0x95C2|0x95C4|0x95C5|0x95C6|0x95C7|0x95C9|0x95CC|0x95CD|0x95CE|0x95CF|0x9610|0x9611|0x9612|0x9613|0x9614|0x9615|0x9616|0x9640|0x9641|0x9642|0x9643|0x9644|0x9645|0x9647|0x9648|0x9649|0x964A|0x964B|0x964C|0x964E|0x964F|0x9710|0x9711|0x9712|0x9713|0x9714|0x9715|0x9802|0x9803|0x9804|0x9805|0x9806|0x9807|0x9808|0x9809|0x980A|0x9830|0x9831|0x9832|0x9833|0x9834|0x9835|0x9836|0x9837|0x9838|0x9839|0x983A|0x983B|0x983C|0x983D|0x983E|0x983F|0x9850|0x9851|0x9852|0x9853|0x9854|0x9855|0x9856|0x9857|0x9858|0x9859|0x985A|0x985B|0x985C|0x985D|0x985E|0x985F|0x9900|0x9901|0x9903|0x9904|0x9905|0x9906|0x9907|0x9908|0x9909|0x990A|0x990B|0x990C|0x990D|0x990E|0x990F|0x9910|0x9913|0x9917|0x9918|0x9919|0x9990|0x9991|0x9992|0x9993|0x9994|0x9995|0x9996|0x9997|0x9998|0x9999|0x999A|0x999B|0x999C|0x999D|0x99A0|0x99A2|0x99A4"

# NVIDIA driver card lists
NVIDIA_LATEST_CARDS="GeForce RTX 5090 Laptop GPU|GeForce RTX 5080 Laptop GPU|GeForce RTX 5070 Ti Laptop GPU|GeForce RTX 5070 Laptop GPU|GeForce RTX 5060 Laptop GPU|GeForce RTX 5050 Laptop GPU|GeForce RTX 5090 D v2|GeForce RTX 5090 D|GeForce RTX 5090|GeForce RTX 5080|GeForce RTX 5070 Ti|GeForce RTX 5070|GeForce RTX 5060 Ti|GeForce RTX 5060|GeForce RTX 5050|GeForce RTX 4090 Laptop GPU|GeForce RTX 4080 Laptop GPU|GeForce RTX 4070 Laptop GPU|GeForce RTX 4060 Laptop GPU|GeForce RTX 4050 Laptop GPU|GeForce RTX 4090 D|GeForce RTX 4090|GeForce RTX 4080 SUPER|GeForce RTX 4080|GeForce RTX 4070 Ti SUPER|GeForce RTX 4070 Ti|GeForce RTX 4070 SUPER|GeForce RTX 4070|GeForce RTX 4060 Ti|GeForce RTX 4060|GeForce RTX 3080 Ti Laptop GPU|GeForce RTX 3080 Laptop GPU|GeForce RTX 3070 Ti Laptop GPU|GeForce RTX 3070 Laptop GPU|GeForce RTX 3060 Laptop GPU|GeForce RTX 3050 Ti Laptop GPU|GeForce RTX 3050 Laptop GPU|GeForce RTX 3090 Ti|GeForce RTX 3090|GeForce RTX 3080 Ti|GeForce RTX 3080|GeForce RTX 3070 Ti|GeForce RTX 3070|GeForce RTX 3060 Ti|GeForce RTX 3060|GeForce RTX 3050|GeForce RTX 2080 SUPER|GeForce RTX 2080|GeForce RTX 2070 SUPER|GeForce RTX 2070|GeForce RTX 2060|GeForce RTX 2050|GeForce RTX 2080 Ti|GeForce RTX 2080 SUPER|GeForce RTX 2080|GeForce RTX 2070 SUPER|GeForce RTX 2070|GeForce RTX 2060 SUPER|GeForce RTX 2060|GeForce MX570|GeForce MX550|GeForce MX450|GeForce MX350|GeForce MX330|GeForce MX250|GeForce MX230|GeForce MX150|GeForce MX130|GeForce MX110|GeForce GTX 1660 Ti|GeForce GTX 1650 Ti|GeForce GTX 1650|GeForce GTX 1660 SUPER|GeForce GTX 1650 SUPER|GeForce GTX 1660 Ti|GeForce GTX 1660|GeForce GTX 1650|GeForce GTX 1630|GeForce GTX 1080 Ti|GeForce GTX 1080|GeForce GTX 1070 Ti|GeForce GTX 1070|GeForce GTX 1060|GeForce GTX 1050 Ti|GeForce GTX 1050|GeForce GT 1030|GeForce GT 1010|GeForce GTX 1080|GeForce GTX 1070|GeForce GTX 1060|GeForce GTX 1050 Ti|GeForce GTX 1050|GeForce GTX 980 Ti|GeForce GTX 980|GeForce GTX 970|GeForce GTX 960|GeForce GTX 950|GeForce GTX 980|GeForce GTX 980M|GeForce GTX 970M|GeForce GTX 965M|GeForce GTX 960M|GeForce GTX 950M|GeForce 945M|GeForce 940MX|GeForce 930MX|GeForce 920MX|GeForce 940M|GeForce 930M|GeForce GTX 860M|GeForce GTX 850M|GeForce 845M|GeForce 840M|GeForce 830M|GeForce GTX 750 Ti|GeForce GTX 750|GeForce GTX 745|NVIDIA TITAN RTX|NVIDIA TITAN V|NVIDIA TITAN Xp|NVIDIA TITAN X (Pascal)|GeForce GTX TITAN X|NVIDIA RTX PRO 6000 Blackwell Workstation|NVIDIA RTX PRO 6000 Blackwell Max-Q Workstation|NVIDIA RTX PRO 5000 72GB Blackwell|NVIDIA RTX PRO 4500 Blackwell Workstation Edition|NVIDIA RTX PRO 4000 Blackwell SFF Edition|NVIDIA RTX PRO 4000 Blackwell|NVIDIA RTX PRO 2000 Blackwell|NVIDIA RTX PRO 5000 Blackwell Laptop GPU|NVIDIA RTX PRO 4000 Blackwell Laptop GPU|NVIDIA RTX PRO 3000 Blackwell Laptop GPU|NVIDIA RTX PRO 2000 Blackwell Laptop GPU|NVIDIA RTX PRO 1000 Blackwell Laptop GPU|NVIDIA RTX PRO 500 Blackwell Laptop GPU|NVIDIA RTX 6000D|NVIDIA RTX 6000 Ada Generation|NVIDIA RTX 5880 Ada Generation|NVIDIA RTX 5000 Ada Generation|NVIDIA RTX 4500 Ada Generation|NVIDIA RTX 4000 Ada Generation|NVIDIA RTX 4000 SFF Ada Generation|NVIDIA RTX 2000 Ada Generation|NVIDIA RTX 2000E Ada Generation|NVIDIA RTX A6000|NVIDIA RTX A5500|NVIDIA RTX A5000|NVIDIA RTX A4500|NVIDIA RTX A4000H|NVIDIA RTX A4000|NVIDIA RTX A2000 12GB|NVIDIA RTX A2000|NVIDIA RTX A1000|NVIDIA RTX A400|NVIDIA A800 40GB Active|NVIDIA T1000 8GB|NVIDIA T1000|NVIDIA T600|NVIDIA T400 4GB|NVIDIA T400|NVIDIA T400E|NVIDIA RTX 5000 Ada Generation Laptop GPU|NVIDIA RTX 4000 Ada Generation Laptop GPU|NVIDIA RTX 3500 Ada Generation Laptop GPU|NVIDIA RTX 3000 Ada Generation Laptop GPU|NVIDIA RTX 2000 Ada Generation Laptop GPU|NVIDIA RTX 1000 Ada Generation Laptop GPU|NVIDIA RTX 500 Ada Generation Laptop GPU|NVIDIA RTX A5500 Laptop GPU|NVIDIA RTX A5000 Laptop GPU|NVIDIA RTX A4500 Laptop GPU|NVIDIA RTX A4000 Laptop GPU|NVIDIA RTX A3000 12GB Laptop GPU|NVIDIA RTX A3000 Laptop GPU|NVIDIA RTX A2000 8GB Laptop GPU|NVIDIA RTX A2000 Laptop GPU|NVIDIA RTX A1000 6GB Laptop GPU|NVIDIA RTX A1000 Laptop GPU|NVIDIA RTX A500 Laptop GPU|NVIDIA T1200 Laptop GPU|NVIDIA T600 Laptop GPU|NVIDIA T550 Laptop GPU|NVIDIA T500|Quadro RTX 8000|Quadro RTX 6000|Quadro RTX 5000|Quadro RTX 4000|Quadro RTX 3000|Quadro GV100|Quadro GP100|Quadro P6000|Quadro P5200|Quadro P5000|Quadro P4000|Quadro P2200|Quadro P2000|Quadro P1000|Quadro P620|Quadro P600|Quadro P400|Quadro M6000 24GB|Quadro M6000|Quadro M5000|Quadro M4000|Quadro M2000|Quadro K2200|Quadro K1200|Quadro K620|Quadro T2000|Quadro T1000|Quadro P5200|Quadro P5000|Quadro P4200|Quadro P3200|Quadro P4000|Quadro P3000|Quadro P2000|Quadro P1000|Quadro P600|Quadro P520|Quadro P500|Quadro M2200|Quadro M1200|Quadro M620|Quadro M520|Quadro M5500|Quadro M5000M|Quadro M4000M|Quadro M3000M|Quadro M2000M|Quadro M1000M|Quadro M600M|Quadro M500M|Quadro K2200M|Quadro K620M|NVS 810"

NVIDIA_580_CARDS="GeForce RTX 5090 Laptop GPU|GeForce RTX 5080 Laptop GPU|GeForce RTX 5070 Ti Laptop GPU|GeForce RTX 5070 Laptop GPU|GeForce RTX 5060 Laptop GPU|GeForce RTX 5050 Laptop GPU|GeForce RTX 5090 D v2|GeForce RTX 5090 D|GeForce RTX 5090|GeForce RTX 5080|GeForce RTX 5070 Ti|GeForce RTX 5070|GeForce RTX 5060 Ti|GeForce RTX 5060|GeForce RTX 5050|GeForce RTX 4090 Laptop GPU|GeForce RTX 4080 Laptop GPU|GeForce RTX 4070 Laptop GPU|GeForce RTX 4060 Laptop GPU|GeForce RTX 4050 Laptop GPU|GeForce RTX 4090 D|GeForce RTX 4090|GeForce RTX 4080 SUPER|GeForce RTX 4080|GeForce RTX 4070 Ti SUPER|GeForce RTX 4070 Ti|GeForce RTX 4070 SUPER|GeForce RTX 4070|GeForce RTX 4060 Ti|GeForce RTX 4060|GeForce RTX 3080 Ti Laptop GPU|GeForce RTX 3080 Laptop GPU|GeForce RTX 3070 Ti Laptop GPU|GeForce RTX 3070 Laptop GPU|GeForce RTX 3060 Laptop GPU|GeForce RTX 3050 Ti Laptop GPU|GeForce RTX 3050 Laptop GPU|GeForce RTX 3090 Ti|GeForce RTX 3090|GeForce RTX 3080 Ti|GeForce RTX 3080|GeForce RTX 3070 Ti|GeForce RTX 3070|GeForce RTX 3060 Ti|GeForce RTX 3060|GeForce RTX 3050|GeForce RTX 2080 SUPER|GeForce RTX 2080|GeForce RTX 2070 SUPER|GeForce RTX 2070|GeForce RTX 2060|GeForce RTX 2050|GeForce RTX 2080 Ti|GeForce RTX 2080 SUPER|GeForce RTX 2080|GeForce RTX 2070 SUPER|GeForce RTX 2070|GeForce RTX 2060 SUPER|GeForce RTX 2060|GeForce MX570|GeForce MX550|GeForce MX450|GeForce MX350|GeForce MX330|GeForce MX250|GeForce MX230|GeForce MX150|GeForce MX130|GeForce MX110|GeForce GTX 1660 Ti|GeForce GTX 1650 Ti|GeForce GTX 1650|GeForce GTX 1660 SUPER|GeForce GTX 1650 SUPER|GeForce GTX 1660 Ti|GeForce GTX 1660|GeForce GTX 1650|GeForce GTX 1630|GeForce GTX 1080 Ti|GeForce GTX 1080|GeForce GTX 1070 Ti|GeForce GTX 1070|GeForce GTX 1060|GeForce GTX 1050 Ti|GeForce GTX 1050|GeForce GT 1030|GeForce GT 1010|GeForce GTX 1080|GeForce GTX 1070|GeForce GTX 1060|GeForce GTX 1050 Ti|GeForce GTX 1050|GeForce GTX 980 Ti|GeForce GTX 980|GeForce GTX 970|GeForce GTX 960|GeForce GTX 950|GeForce GTX 980|GeForce GTX 980M|GeForce GTX 970M|GeForce GTX 965M|GeForce GTX 960M|GeForce GTX 950M|GeForce 945M|GeForce 940MX|GeForce 930MX|GeForce 920MX|GeForce 940M|GeForce 930M|GeForce GTX 860M|GeForce GTX 850M|GeForce 845M|GeForce 840M|GeForce 830M|GeForce GTX 750 Ti|GeForce GTX 750|GeForce GTX 745|NVIDIA TITAN RTX|NVIDIA TITAN V|NVIDIA TITAN Xp|NVIDIA TITAN X (Pascal)|GeForce GTX TITAN X|NVIDIA RTX PRO 6000 Blackwell Workstation|NVIDIA RTX PRO 6000 Blackwell Max-Q Workstation|NVIDIA RTX PRO 5000 72GB Blackwell|NVIDIA RTX PRO 4500 Blackwell Workstation Edition|NVIDIA RTX PRO 4000 Blackwell SFF Edition|NVIDIA RTX PRO 4000 Blackwell|NVIDIA RTX PRO 2000 Blackwell|NVIDIA RTX PRO 5000 Blackwell Laptop GPU|NVIDIA RTX PRO 4000 Blackwell Laptop GPU|NVIDIA RTX PRO 3000 Blackwell Laptop GPU|NVIDIA RTX PRO 2000 Blackwell Laptop GPU|NVIDIA RTX PRO 1000 Blackwell Laptop GPU|NVIDIA RTX PRO 500 Blackwell Laptop GPU|NVIDIA RTX 6000D|NVIDIA RTX 6000 Ada Generation|NVIDIA RTX 5880 Ada Generation|NVIDIA RTX 5000 Ada Generation|NVIDIA RTX 4500 Ada Generation|NVIDIA RTX 4000 Ada Generation|NVIDIA RTX 4000 SFF Ada Generation|NVIDIA RTX 2000 Ada Generation|NVIDIA RTX 2000E Ada Generation|NVIDIA RTX A6000|NVIDIA RTX A5500|NVIDIA RTX A5000|NVIDIA RTX A4500|NVIDIA RTX A4000H|NVIDIA RTX A4000|NVIDIA RTX A2000 12GB|NVIDIA RTX A2000|NVIDIA RTX A1000|NVIDIA RTX A400|NVIDIA A800 40GB Active|NVIDIA T1000 8GB|NVIDIA T1000|NVIDIA T600|NVIDIA T400 4GB|NVIDIA T400|NVIDIA T400E|NVIDIA RTX 5000 Ada Generation Laptop GPU|NVIDIA RTX 4000 Ada Generation Laptop GPU|NVIDIA RTX 3500 Ada Generation Laptop GPU|NVIDIA RTX 3000 Ada Generation Laptop GPU|NVIDIA RTX 2000 Ada Generation Laptop GPU|NVIDIA RTX 1000 Ada Generation Laptop GPU|NVIDIA RTX 500 Ada Generation Laptop GPU|NVIDIA RTX A5500 Laptop GPU|NVIDIA RTX A5000 Laptop GPU|NVIDIA RTX A4500 Laptop GPU|NVIDIA RTX A4000 Laptop GPU|NVIDIA RTX A3000 12GB Laptop GPU|NVIDIA RTX A3000 Laptop GPU|NVIDIA RTX A2000 8GB Laptop GPU|NVIDIA RTX A2000 Laptop GPU|NVIDIA RTX A1000 6GB Laptop GPU|NVIDIA RTX A1000 Laptop GPU|NVIDIA RTX A500 Laptop GPU|NVIDIA T1200 Laptop GPU|NVIDIA T600 Laptop GPU|NVIDIA T550 Laptop GPU|NVIDIA T500|Quadro RTX 8000|Quadro RTX 6000|Quadro RTX 5000|Quadro RTX 4000|Quadro RTX 3000|Quadro GV100|Quadro GP100|Quadro P6000|Quadro P5200|Quadro P5000|Quadro P4000|Quadro P2200|Quadro P2000|Quadro P1000|Quadro P620|Quadro P600|Quadro P400|Quadro M6000 24GB|Quadro M6000|Quadro M5000|Quadro M4000|Quadro M2000|Quadro K2200|Quadro K1200|Quadro K620|Quadro T2000|Quadro T1000|Quadro P5200|Quadro P5000|Quadro P4200|Quadro P3200|Quadro P4000|Quadro P3000|Quadro P2000|Quadro P1000|Quadro P600|Quadro P520|Quadro P500|Quadro M2200|Quadro M1200|Quadro M620|Quadro M520|Quadro M5500|Quadro M5000M|Quadro M4000M|Quadro M3000M|Quadro M2000M|Quadro M1000M|Quadro M600M|Quadro M500M|Quadro K2200M|Quadro K620M|NVS 810"

NVIDIA_470_CARDS="NVIDIA TITAN RTX|NVIDIA TITAN V|NVIDIA TITAN Xp|NVIDIA TITAN X (Pascal)|GeForce GTX TITAN X|GeForce GTX TITAN|GeForce GTX TITAN Black|GeForce GTX TITAN Z|GeForce RTX 3080 Laptop GPU|GeForce RTX 3070 Laptop GPU|GeForce RTX 3060 Laptop GPU|GeForce RTX 3050 Ti Mobile|GeForce RTX 3050 Ti Laptop GPU|GeForce RTX 3050 Laptop GPU|GeForce RTX 3090|GeForce RTX 3080 Ti|GeForce RTX 3080|GeForce RTX 3070 Ti|GeForce RTX 3070|GeForce RTX 3060 Ti|GeForce RTX 3060|GeForce RTX 2080 SUPER|GeForce RTX 2080|GeForce RTX 2070 SUPER|GeForce RTX 2070|GeForce RTX 2060|GeForce RTX 2050|GeForce RTX 2080 Ti|GeForce RTX 2080 SUPER|GeForce RTX 2080|GeForce RTX 2070 SUPER|GeForce RTX 2070|GeForce RTX 2060 SUPER|GeForce RTX 2060|GeForce MX570|GeForce MX550|GeForce MX450|GeForce MX350|GeForce MX330|GeForce MX250|GeForce MX230|GeForce MX150|GeForce MX130|GeForce MX110|GeForce GTX 1660 Ti|GeForce GTX 1650 Ti|GeForce GTX 1650|GeForce GTX 1660 SUPER|GeForce GTX 1650 SUPER|GeForce GTX 1660 Ti|GeForce GTX 1660|GeForce GTX 1650|GeForce GTX 1080 Ti|GeForce GTX 1080|GeForce GTX 1070 Ti|GeForce GTX 1070|GeForce GTX 1060|GeForce GTX 1050 Ti|GeForce GTX 1050|GeForce GT 1030|GeForce GT 1010|GeForce GTX 1080|GeForce GTX 1070|GeForce GTX 1060|GeForce GTX 1050 Ti|GeForce GTX 1050|GeForce GTX 980 Ti|GeForce GTX 980|GeForce GTX 970|GeForce GTX 960|GeForce GTX 950|GeForce GTX 980|GeForce GTX 980M|GeForce GTX 970M|GeForce GTX 965M|GeForce GTX 960M|GeForce GTX 950M|GeForce 945M|GeForce 940MX|GeForce 930MX|GeForce 920MX|GeForce 940M|GeForce 930M|GeForce GTX 860M|GeForce GTX 850M|GeForce 845M|GeForce 840M|GeForce 830M|GeForce GTX 780 Ti|GeForce GTX 780|GeForce GTX 770|GeForce GTX 760|GeForce GTX 760 Ti (OEM)|GeForce GTX 750 Ti|GeForce GTX 750|GeForce GTX 745|GeForce GT 740|GeForce GT 730|GeForce GT 720|GeForce GT 710|GeForce GTX 690|GeForce GTX 680|GeForce GTX 670|GeForce GTX 660 Ti|GeForce GTX 660|GeForce GTX 650 Ti BOOST|GeForce GTX 650 Ti|GeForce GTX 650|GeForce GTX 645|GeForce GT 640|GeForce GT 635|NVIDIA RTX A6000|NVIDIA RTX A5000|NVIDIA RTX A4500|NVIDIA RTX A4000|NVIDIA RTX A2000 12GB|NVIDIA RTX A2000|NVIDIA T1000 8GB|NVIDIA T1000|NVIDIA T600|NVIDIA T400 4GB|NVIDIA T400|NVIDIA RTX A5000 Laptop GPU|NVIDIA RTX A4000 Laptop GPU|NVIDIA RTX A3000 Laptop GPU|NVIDIA RTX A2000 Laptop GPU|NVIDIA T1200 Laptop GPU |NVIDIA T600 Laptop GPU|NVIDIA T500|Quadro RTX 8000|Quadro RTX 6000|Quadro RTX 5000|Quadro RTX 4000|Quadro RTX 3000|Quadro RTX 6000|Quadro GV100|Quadro GP100|Quadro P6000|Quadro P5200|Quadro P5000|Quadro P4000|Quadro P2200|Quadro P2000|Quadro P1000|Quadro P620|Quadro P600|Quadro P400|Quadro M6000 24GB|Quadro M6000|Quadro M5000|Quadro M4000|Quadro M2000|Quadro K6000|Quadro K5200|Quadro K5000|Quadro K4000|Quadro K4200|Quadro K2200|Quadro K2000|Quadro K2000D|Quadro K1200|Quadro K620|Quadro K600|Quadro K420|Quadro 410|Quadro T2000|Quadro T1000|Quadro P5200|Quadro P5000|Quadro P4200|Quadro P3200|Quadro P4000|Quadro P3000|Quadro P2000|Quadro P1000|Quadro P600|Quadro P520|Quadro P500|Quadro M2200|Quadro M1200|Quadro M620|Quadro M520|Quadro M5500|Quadro M5000M|Quadro M4000M|Quadro M3000M|Quadro M2000M|Quadro M1000M|Quadro M600M|Quadro M500M|Quadro K2200M|Quadro K620M|Quadro P5000|Quadro P3000|Quadro M5000 SE|Quadro M3000 SE|NVS 810|NVS 510|GRID K520|NVS 810|NVS 510"

NVIDIA_390_CARDS="GeForce GTX 480|GeForce GTX 465|GeForce GTX 480M|GeForce GTX 470|GeForce GT 440|GeForce GTS 450|GeForce GTS 450|GeForce GTS 450|GeForce GT 555M|GeForce GT 555M|GeForce GTX 460M|GeForce GT 445M|GeForce GT 435M|GeForce GT 550M|GeForce GT 440|GeForce GT 430|GeForce GT 420|GeForce GT 635M|GeForce GT 520|GeForce GT 530|GeForce GT 610|GeForce GT 620M|GeForce GT 630M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 640M LE|GeForce GT 635M|GeForce 610M|GeForce 615|GeForce 615|GeForce 615|GeForce 615|GeForce 615|GeForce GT 555M|GeForce GT 525M|GeForce GT 520M|GeForce GT 415M|GeForce GT 425M|GeForce GT 420M|GeForce GT 435M|GeForce GT 420M|GeForce GT 540M|GeForce GT 630M|GeForce GT 630M|GeForce GT 525M|GeForce GT 550M|GeForce GT 520M|GeForce GTX 460|GeForce GTX 460 SE|GeForce GTX 460|GeForce GTX 470M|GeForce GTX 485M|GeForce GT 630|GeForce GT 620|GeForce GT 730|GeForce GT 610|GeForce GT 640|GeForce GT 640|GeForce GT 630|GeForce GTX 650|GeForce GT 740|GeForce GT 730|GeForce GT 755M|GeForce GT 640M LE|GeForce GT 650M|GeForce GT 640M|GeForce GT 640M LE|GeForce GT 640M LE|GeForce GT 640M LE|GeForce GTX 660M|GeForce GT 650M|GeForce GT 640M|GeForce GT 645M|GeForce GT 740M|GeForce GTX 660M|GeForce GT 730M|GeForce GT 745M|GeForce GT 745M|GeForce GT 745A|GeForce GT 745A|GeForce GT 750M|GeForce GT 750M|GeForce GT 755M|GeForce 710A|GeForce 820M|GeForce 810M|GeForce GTX TITAN Z|GeForce GTX 780|GeForce GTX TITAN|GeForce GTX 780|GeForce GTX 780 Ti|GeForce GTX 780 Ti|GeForce GTX TITAN Black|GeForce GT 520|GeForce 510|GeForce 605|GeForce GT 620|GeForce GT 610|GeForce GT 625 (OEM)|GeForce GT 625|GeForce GT 625|GeForce GT 625|GeForce GT 625|GeForce GT 625|GeForce GT 705|GeForce GT 520M|GeForce GT 520MX|GeForce GT 520M|GeForce 410M|GeForce 410M|GeForce 610M|GeForce 610|GeForce 800A|GeForce  705A|GeForce 800A|GeForce 800A|GeForce 800A|GeForce 800A|GeForce 800A|GeForce 610M|GeForce 610M|GeForce 705M|GeForce 705A|GeForce 800A|GeForce 705A|GeForce 800A|GeForce GTX 580|GeForce GTX 570|GeForce GTX 560 Ti|GeForce GTX 560|GeForce GTX 570|GeForce GTX 560 Ti|GeForce GTX 590|GeForce GTX 580|GeForce GTX 580|GeForce 820M|GeForce GT 720M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 620M|GeForce GT 720M|GeForce 710M|GeForce 710M|GeForce GT 720M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce 820M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce 820M|GeForce 710M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 810M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 810M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce GT 630M|GeForce GT 630M|GeForce GT 620M|GeForce GT 620M|GeForce GT 625M|GeForce GT 630M|GeForce GT 630M|GeForce GT 630M|GeForce GT 625M|GeForce GT 625M|GeForce GT 625M|GeForce GT 625M|GeForce GT 625M|GeForce GT 625M|GeForce GT 630M|GeForce GT 720M|GeForce GT 720M|GeForce GT 630M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce GT 630M|GeForce GT 630M|GeForce GT 630M|GeForce GT 630M|GeForce GT 630M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce GT 720A|GeForce 710A|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 620M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce GT 620M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 710M|GeForce GT 710M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce GT 720M|GeForce GT 720M|GeForce 820M|GeForce 820M|GeForce GT 720M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce GT 620M|GeForce GT 630M|GeForce GT 620M|GeForce 820M|GeForce GT 620M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 820M|GeForce 820M|GeForce GT 710M|GeForce GT 720M|GeForce 820M|GeForce 720M|GeForce GT 720M|GeForce 820M|GeForce GT 720M|GeForce GT 720M|GeForce 720M|GeForce GT 720M|GeForce 820M|GeForce GT 720M|GeForce 820M|GeForce 620M|GeForce GT 630M|GeForce GT 630M|GeForce GT 820M|GeForce 710M|GeForce 820M|GeForce GT 630M|GeForce 710M|GeForce GT 720M|GeForce 820M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720A|GeForce 820A|GeForce 720A|GeForce 820A|GeForce 820A|GeForce 820A|GeForce 820A|GeForce GT 620M|GeForce 705M|GeForce 800M|GeForce 820A|GeForce 800M|GeForce 800M|GeForce 800M|GeForce 705A|GeForce 820A|GeForce 820A|GeForce 820A|GeForce 820A|GeForce 820A|GeForce 820A|GeForce 820A|GeForce 820A|GeForce 820A|GeForce 820A|GeForce 820A|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 720M|GeForce GT 820M|GeForce GT 820M|GeForce GT 820M|GeForce GT 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce 610M|GeForce 710M|GeForce 710M|GeForce GT 625M|GeForce GT 720M|GeForce 820M|GeForce GT 720M|GeForce GT 720M|GeForce 820M|GeForce 820M|GeForce 610M|GeForce 610M|GeForce GT 720M|GeForce 705M|GeForce GT 620M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce GT 720M|GeForce 710M|GeForce 710M|GeForce 710M|GeForce GT 720M|GeForce 710M|GeForce GT 720M|GeForce GT 720M|GeForce 705M|GeForce 705M|GeForce 820M|GeForce 820M|GeForce 710M|GeForce 820M|GeForce 820M|GeForce 710M|GeForce 710M|GeForce GT 720M|GeForce GT 720M|GeForce 820M|GeForce 820M|GeForce GT 620M|GeForce GT 620M|GeForce 820M|GeForce GT 720M|GeForce 820M|GeForce 820M|GeForce 820M|GeForce GT 720M|GeForce 820M|GeForce 810M|GeForce GTX 680|GeForce GTX 660 Ti|GeForce GTX 770|GeForce GTX 660|GeForce GTX 760|GeForce GTX 760|GeForce GTX 690|GeForce GTX 670|GeForce GTX 760 Ti OEM|GeForce GTX 760 (192-bit)|GeForce GTX 760 Ti OEM|GeForce GTX 660|GeForce GTX 880M|GeForce GTX 870M|GeForce GTX 760|GeForce GTX 860M|GeForce GTX 775M|GeForce GTX 780M|GeForce GTX 780M|GeForce GTX 680M|GeForce GTX 670MX|GeForce GTX 675MX|GeForce GTX 680MX|GeForce GTX 675MX|GeForce GTX 660|GeForce GTX 650 Ti BOOST|GeForce GTX 650 Ti|GeForce GTX 645|GeForce GT 740|GeForce GTX 650 Ti|GeForce GTX 650|GeForce GT 740|GeForce GTX 770M|GeForce GTX 765M|GeForce GTX 765M|GeForce GTX 760M|GeForce GTX 760A|GeForce GTX 560 Ti|GeForce GTX 560|GeForce GTX 460 SE v2|GeForce GTX 460 v2|GeForce GTX 555|GeForce GT 645|GeForce GTX 560 SE|GeForce GTX 570M|GeForce GTX 580M|GeForce GTX 675M|GeForce GTX 670M|GeForce GT 545|GeForce GT 545|GeForce GTX 550 Ti|GeForce GTS 450|GeForce GT 550M|GeForce GT 555M|GeForce GT 635M|GeForce GT 635M|GeForce GT 635M|GeForce GT 555M|GeForce GTS 450|GeForce GT 640|GeForce GT 555M|GeForce GT 635M|GeForce GTX 560M|GeForce GT 635|GeForce GT 710|GeForce GT 640|GeForce GT 630|GeForce GT 720|GeForce GT 730|GeForce GT 720|GeForce GT 710|GeForce GT 710|GeForce GT 730M|GeForce 730A|GeForce GT 735M|GeForce GT 740M|GeForce GT 740A|GeForce GT 740A|GeForce GT 740A|GeForce GT 730M|GeForce 710M|GeForce 710A|GeForce 710A|GeForce 810A|GeForce 810A|GeForce 805A|GeForce 710A|GeForce 825M|GeForce GT 720M|GeForce 920M|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce 920A|GeForce GT 730|GeForce 910M|GeForce 830M|GeForce 830A|GeForce 840M|GeForce 840A|GeForce 840A|GeForce 840A|GeForce 840A|GeForce 845M|GeForce 930M|GeForce 930A|GeForce 930A|GeForce 930A|GeForce 930A|GeForce 940M|GeForce 940A|GeForce 940A|GeForce 945M|GeForce 945A|GeForce 930M|GeForce 930A|GeForce 930A|GeForce 930A|GeForce 930A|GeForce 930A|GeForce 940MX|GeForce GPU|GeForce 940MX|GeForce 930MX|GeForce 920MX|GeForce 940A|GeForce GTX 750 Ti|GeForce GTX 750|GeForce GTX 745|GeForce 845M|GeForce GTX 850M|GeForce GTX 850A|GeForce GTX 860M|GeForce GPU|GeForce GTX 750 Ti|GeForce GTX 750 Ti|GeForce 840M|GeForce 845M|GeForce 945M|GeForce GTX 950M|GeForce GTX 950A|GeForce GTX 950A|GeForce GTX 950A|GeForce GTX 950A|GeForce GTX 950A|GeForce GTX 950A|GeForce GTX 960M|GeForce GTX 750 Ti|GeForce GTX 860M|GeForce GTX 960A|GeForce GTX 750Ti|GeForce GTX 960A|GeForce GTX 750 Ti|GeForce GTX 750Ti|GeForce 940M|GeForce GTX 750 Ti|GeForce GTX 980|GeForce GTX 970|GeForce GTX 980M|GeForce GTX 970M|GeForce GTX 960|GeForce GTX 960|GeForce GTX 960|GeForce GTX 960|GeForce GTX 960|GeForce GTX 965M|GeForce GTX 980|GeForce GTX 960|GeForce GTX 950|GeForce GTX 960|GeForce GTX 750|GeForce GTX 965M|GeForce GTX 950|GeForce GTX 980M|GeForce GTX 970M|GeForce GTX 965M|GeForce GTX 980|GeForce GTX 965M|GeForce MX130|GeForce MX110|GeForce 940MX|GeForce GTX TITAN X|GeForce GTX 980 Ti|TITAN X (Pascal)|TITAN Xp|TITAN Xp COLLECTORS EDITION|TITAN Xp COLLECTORS EDITION|GeForce GTX 1080 Ti|GeForce GTX 1080|GeForce GTX 1070|GeForce GTX 1070 Ti|GeForce GTX 1060 6GB|GeForce GTX 1060 3GB|P104-100|GeForce GTX 1080|GeForce GTX 1080 with Max-Q Design|GeForce GTX 1070|GeForce GTX 1070 with MaxQ Design|GeForce GTX 1070 With Max-Q Design|GeForce GTX 1070 with Max-Q Design|GeForce GTX 1070 with Max-Q Design|GeForce GTX 1070 with Max-Q Design|GeForce GTX 1070 with Max-Q Design|GeForce GTX 1070 with Max-Q Design|GeForce GTX 1070 with Max-Q Design|GeForce GTX 1070 with Max-Q Design|GeForce GTX 1070 with Max-Q Design|P104-101|GeForce GTX 1080|GeForce GTX 1080 with Max-Q Design|GeForce GTX 1080 with Max-Q Design|GeForce GTX 1080 with Max-Q Design|GeForce GTX 1080 with Max-Q Design|GeForce GTX 1080 with Max-Q Design|GeForce GTX 1080 with Max-Q Design|GeForce GTX 1080 with Max-Q Design|GeForce GTX 1080 with Max-Q Design|GeForce GTX 1070|GeForce GTX 1070 with Max-Q Design|GeForce GTX 1070 with Max-Q Design|GeForce GTX 1070 with Max-Q Design|GeForce GTX 1060 3GB|GeForce GTX 1060 6GB|GeForce GTX 1060 5GB|GeForce GTX 1060 6GB|P106-100|P106-090|GeForce GTX 1060|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1050 Ti|GeForce GTX 1050|GeForce GTX 1060|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1060 with Max-Q Design|GeForce GTX 1050 Ti|GeForce GTX 1050|GeForce GTX 1050|GeForce GTX 1050 Ti|GeForce GTX 1050|GeForce GTX 1050 Ti|GeForce GTX 1050 Ti with Max-Q Design|GeForce GTX 1050 Ti with Max-Q Design|GeForce GTX 1050 Ti with Max-Q Design|GeForce GTX 1050|GeForce GTX 1050 with Max-Q Design|GeForce GTX 1050 with Max-Q Design|GeForce GTX 1050 with Max-Q Design|GeForce GTX 1050 with Max-Q Design|GeForce GT 1030|GeForce MX150|GeForce MX150|TITAN V|Quadro 6000|Quadro 5000|Quadro 5000M|Quadro 6000|Quadro 4000|Quadro 2000|Quadro 2000D|Quadro 2000M|Quadro 600|Quadro 500M|Quadro 1000M|Quadro 3000M|Quadro 4000M|Quadro K420|Quadro K1100M|Quadro K500M|Quadro K2000D|Quadro K600|Quadro K2000M|Quadro K1000M|Quadro K2000|Quadro 410|Quadro K6000|Quadro K5200|Quadro 5010M|Quadro 7000|Quadro K4200|Quadro K3100M|Quadro K4100M|Quadro K5100M|Quadro K5000|Quadro K5000M|Quadro K4000M|Quadro K3000M|Quadro K4000|Quadro K2100M|Quadro K610M|Quadro K510M|Quadro K620M|Quadro M500M|Quadro M500M|Quadro M520|Quadro M2000M|Quadro M1000M|Quadro M600M|Quadro K2200M|Quadro M620|Quadro M1200|Quadro K2200|Quadro K620|Quadro K1200|Quadro M5000|Quadro M4000|Quadro M5000M|Quadro M5000 SE|Quadro M4000M|Quadro M3000M|Quadro M3000 SE|Quadro M5500|Quadro M2000|Quadro GP100|Quadro M6000|Quadro M6000 24GB|Quadro P6000|Quadro P5000|Quadro P4000|Quadro P5200|Quadro P5000|Quadro P4000|Quadro P4000 with Max-Q Design|Quadro P4000 with Max-Q Design|Quadro P3000|Quadro P4200|Quadro P3200|Quadro P2000|Quadro P1000|Quadro P600|Quadro P400|Quadro P620|Quadro P2000|Quadro P1000|Quadro P600|Quadro P500|Quadro GV100|NVS 5400M|NVS 5200M|NVS 510|NVS 4200M|NVS 4200M|NVS 315|NVS 310|NVS 5200M|NVS 5200M|NVS 5200M|NVS 5200M|NVS 5200M|NVS 5200M|NVS 5200M|NVS 810|Tesla C2050 / C2070|Tesla C2050|Tesla C2070|Tesla M2070|Tesla X2070|Tesla T20 Processor|Tesla S2050|Tesla M2050|Tesla X2070|Tesla M2050|Tesla M2050|Tesla M2050|Tesla M2050|Tesla M2050|Tesla M2070-Q|Tesla K20Xm|Tesla K20c|Tesla K40m|Tesla K40c|Tesla K20s|Tesla K40st|Tesla K20m|Tesla K40s|Tesla K40t|Tesla K80|Tesla M2090|Tesla X2090|Tesla X2090|Tesla X2090|Tesla X2090|Tesla M2075|Tesla C2075|Tesla C2050|Tesla K10|Tesla K8|Tesla M60|Tesla M6|Tesla M4|Quadro M2200|Tesla P100-PCIE-12GB|Tesla P100-PCIE-16GB|Tesla P100-SXM2-16GB|Tesla M40|Tesla M40 24GB|Tesla P40|Tesla P4|Tesla P6|Tesla V100-SXM2-16GB|Tesla V100-FHHL-16GB|Tesla V100-PCIE-16GB|Tesla V100-SXM2-32GB|Tesla V100-PCIE-32GB|Tesla V100-DGXS-32GB|GRID K520"

NVIDIA_340_CARDS="GeForce 8800 GTX|GeForce 8800 GTS|GeForce 8800 Ultra|Tesla C870|Quadro FX 5600|Quadro FX 4600|GeForce 8600 GTS|GeForce 8600 GT|GeForce 8600 GT|GeForce 8600 GS|GeForce 8400 GS|GeForce 9500M GS|GeForce 8300 GS|GeForce 8600M GT|GeForce 9650M GS|GeForce 8700M GT|Quadro FX 370|Quadro NVS 320M|Quadro FX 570M|Quadro FX 1600M|Quadro FX 570|Quadro FX 1700|GeForce GT 330|GeForce 8400 SE|GeForce 8500 GT|GeForce 8400 GS|GeForce 8300 GS|GeForce 8400 GS|GeForce 8600M GS|GeForce 8400M GT|GeForce 8400M GS|GeForce 8400M G|Quadro NVS 140M|Quadro NVS 130M|Quadro NVS 135M|GeForce 9400 GT|Quadro FX 360M|GeForce 9300M G|Quadro NVS 290|GeForce GTX 295|GeForce GTX 280|GeForce GTX 260|GeForce GTX 285|GeForce GTX 275|Tesla C1060|Tesla T10 Processor|Tesla T10 Processor|Tesla M1060|Tesla M1060|Tesla M1060|GeForce GTX 260|GeForce GTX 295|Quadroplex 2200 D2|Quadroplex 2200 S4|Quadro CX|Quadro FX 5800|Quadro FX 4800|Quadro FX 3800|GeForce 8800 GTS 512|GeForce 9800 GT|GeForce 8800 GT|GeForce GT 230|GeForce 9800 GX2|GeForce 9800 GT|GeForce 8800 GS|GeForce GTS 240|GeForce 9800M GTX|GeForce 8800M GTS|GeForce 8800 GS|GeForce GTX 280M|GeForce 9800M GT|GeForce 8800M GTX|GeForce 8800 GS|GeForce GTX 285M|GeForce 9600 GSO|GeForce 8800 GT|GeForce 9800 GTX/9800 GTX+|GeForce 9800 GTX+|GeForce 9800 GT|GeForce GTS 250|GeForce 9800M GTX|GeForce GTX 260M|Quadro FX 4700 X2|Quadro FX 3700|Quadro VX 200|Quadro FX 3600M|Quadro FX 2800M|Quadro FX 3700M|Quadro FX 3800M|GeForce GT 230|GeForce 9600 GT|GeForce 9600 GS|GeForce 9600 GSO 512|GeForce GT 130|GeForce GT 140|GeForce 9800M GTS|GeForce 9700M GTS|GeForce 9800M GS|GeForce 9800M GTS|GeForce 9600 GT|GeForce 9600 GT|GeForce GT 130|GeForce 9700 S|GeForce GTS 160M|GeForce GTS 150M|GeForce 9600 GSO|GeForce 9600 GT|Quadro FX 1800|Quadro FX 2700M|GeForce 9500 GT|GeForce 9400 GT|GeForce 9500 GT|GeForce 9500 GS|GeForce 9500 GS|GeForce GT 120|GeForce 9600M GT|GeForce 9600M GS|GeForce 9600M GT|GeForce GT 220M|GeForce 9700M GT|GeForce 9500M G|GeForce 9650M GT|GeForce G 110M|GeForce GT 130M|GeForce GT 240M LE|GeForce GT 120M|GeForce GT 220M|GeForce GT 320M|GeForce GT 320M|GeForce GT 120|GeForce GT 120|Quadro FX 380|Quadro FX 580|Quadro FX 1700M|GeForce 9400 GT|Quadro FX 770M|GeForce 9300 GE|GeForce 9300 GS|GeForce 8400|GeForce 8400 SE|GeForce 8400 GS|GeForce 9300M GS|GeForce G100|GeForce 9300 SE|GeForce 9200M GS|GeForce 9200M GE|GeForce 9300M GS|Quadro NVS 150M|Quadro NVS 160M|GeForce G 105M|GeForce G 103M|GeForce G105M|Quadro NVS 420|Quadro FX 370 LP|Quadro FX 370 Low Profile|Quadro NVS 450|Quadro FX 370M|Quadro NVS 295|HICx16 + Graphics|HICx8 + Graphics|GeForce 8200M|GeForce 9100M G|GeForce 8200M G|GeForce 9200|GeForce 9100|GeForce 8300|GeForce 8200|nForce 730a|GeForce 9200|nForce 980a/780a SLI|nForce 750a SLI|GeForce 8100 / nForce 720a|GeForce 9400|GeForce 9400|GeForce 9400M G|GeForce 9400M|GeForce 9300|ION|GeForce 9400M G|GeForce 9400M|GeForce 9400|nForce 760i SLI|GeForce 9400|GeForce 9400|GeForce 9300 / nForce 730i|GeForce 9200|GeForce 9100M G|GeForce 8200M G|GeForce 9400M|GeForce 9200|GeForce G102M|GeForce G205M|GeForce G102M|GeForce G205M|ION|ION|GeForce 9400|ION|ION LE|ION LE|GeForce 320M|GeForce 320M|GeForce 320M|GeForce 320M|GeForce 320M|GeForce GT 220|GeForce 315|GeForce 210|GeForce 405|GeForce 405|GeForce GT 230M|GeForce GT 330M|GeForce GT 230M|GeForce GT 330M|NVS 5100M|GeForce GT 320M|GeForce GT 415|GeForce GT 240M|GeForce GT 325M|Quadro 400|Quadro FX 880M|GeForce G210|GeForce 205|GeForce 310|Second Generation ION|GeForce 210|GeForce 310|GeForce 315|GeForce G105M|GeForce G105M|NVS 2100M|NVS 3100M|GeForce 305M|Second Generation ION|Second Generation ION|GeForce 310M|Second Generation ION|Second Generation ION|GeForce 305M|GeForce 310M|GeForce 305M|Second Generation ION|Second Generation ION|GeForce G210M|GeForce G210|GeForce 310M|Second Generation ION|Second Generation ION|Quadro FX 380 LP|GeForce 315M|GeForce 405|GeForce 405|GeForce 405|GeForce 405|GeForce 405|GeForce 405M|GeForce 405M|GeForce 405|GeForce 405|GeForce 405|Quadro FX 380M|GeForce GT 330|GeForce GT 320|GeForce GT 240|GeForce GT 340|GeForce GT 220|GeForce GT 330|GeForce GTS 260M|GeForce GTS 250M|GeForce GT 220|GeForce GT 335M|GeForce GTS 350M|GeForce GTS 360M|Quadro FX 1800M|GeForce 9300 GS|GeForce 8400GS|GeForce 405|NVS 300"


###############################################################################
# Logging
###############################################################################

# Function: log
# Description: Logs a message with timestamp and level to both stderr and log file
# Parameters:
#   $1 - Log level (INFO, WARN, ERROR)
#   $@ - Message to log
# Returns: None
# Usage: log INFO "System configured successfully"
log() {
    level=$1
    shift || true
    ts=$(date '+%Y-%m-%d %H:%M:%S')
    msg=$*
    printf '[%s] [%s] %s\n' "$ts" "$level" "$msg" | tee -a "$LOG_FILE" >&2
}

###############################################################################
# rc.conf management helpers
###############################################################################

# Function: rc_conf_has
# Description: Checks if a key exists in rc.conf or rc.conf.local
# Parameters:
#   $1 - Key name to check
# Returns: 0 if key exists, 1 otherwise
# Usage: if rc_conf_has "vboxguest_enable"; then ...
rc_conf_has() {
    key=$1

    if grep "^$key=" /etc/rc.conf >/dev/null 2>&1 ; then
        return 0
    fi

    if [ -f /etc/rc.conf.local ] && grep "^$key=" /etc/rc.conf.local >/dev/null 2>&1 ; then
        return 0
    fi

    return 1
}

# Function: enable_rc_conf
# Description: Sets or updates an rc.conf value using sysrc
# Parameters:
#   $1 - Key name
#   $2 - Value to set
# Returns: None
# Usage: enable_rc_conf "vboxguest_enable" "YES"
enable_rc_conf() {
    key=$1
    val=$2

    current=$(sysrc -n "$key" 2>/dev/null || echo "")
    if [ "$current" = "$val" ]; then
        log INFO "$key already set to $val"
    else
        if [ -n "$current" ]; then
            log INFO "Updating $key from '$current' to '$val'"
        else
            log INFO "Enabling $key=$val"
        fi
        sysrc "${key}=${val}"
    fi
}

# Function: start_service_safe
# Description: Starts a service if not already running
# Parameters:
#   $1 - Service name
# Returns: 0 on success
# Usage: start_service_safe "vboxguest"
start_service_safe() {
    svc=$1

    if service "$svc" onestatus >/dev/null 2>&1 ; then
        log INFO "$svc already running"
        return 0
    fi

    log INFO "Starting $svc"
    if service "$svc" start >/dev/null 2>&1 ; then
        log INFO "$svc started"
    else
        log WARN "$svc failed to start"
    fi
}

###############################################################################
# Helpers: config directory, backup, application
###############################################################################

# Function: find_config_dir
# Description: Locates the cardDetect templates directory
# Parameters: None
# Returns: Prints directory path to stdout, returns 0 if found, 1 otherwise
# Usage: CONFIG_DIR=$(find_config_dir)
find_config_dir() {
    if [ -d "$LOCAL_CONFIG_DIR" ]; then
        echo "$LOCAL_CONFIG_DIR"
        return 0
    fi
    if [ -d "$SYSTEM_CONFIG_DIR" ]; then
        echo "$SYSTEM_CONFIG_DIR"
        return 0
    fi
    echo ""
    return 1
}

# Function: backup_xorg_conf
# Description: Creates a timestamped backup of existing xorg.conf
# Parameters: None
# Returns: None
# Usage: backup_xorg_conf
backup_xorg_conf() {
    if [ -f "$XORG_CONF" ]; then
        mkdir -p "$BACKUP_DIR"
        ts=$(date '+%Y%m%d_%H%M%S')
        backup="$BACKUP_DIR/xorg.conf.$ts"
        cp "$XORG_CONF" "$backup"
        log INFO "Backed up existing $XORG_CONF to $backup"
    fi
}

# Function: apply_config_template
# Description: Applies an X11 configuration template to /etc/X11/xorg.conf
# Parameters:
#   $1 - Template filename (e.g., "XF86Config.intel")
# Returns: 0 on success, exits on failure
# Usage: apply_config_template "XF86Config.intel"
apply_config_template() {
    template_name=$1

    CONFIG_DIR=$(find_config_dir || true)
    if [ -z "$CONFIG_DIR" ]; then
        log ERROR "No cardDetect directory found. Tried: $LOCAL_CONFIG_DIR and $SYSTEM_CONFIG_DIR"
        log ERROR "Cannot apply template $template_name"
        exit 1
    fi

    template_path="$CONFIG_DIR/$template_name"
    if [ ! -f "$template_path" ]; then
        log ERROR "Template $template_name not found in $CONFIG_DIR"
        exit 1
    fi

    mkdir -p "$(dirname "$XORG_CONF")"
    backup_xorg_conf
    cp "$template_path" "$XORG_CONF"
    log INFO "Applied template $template_name to $XORG_CONF"
    return 0
}

###############################################################################
# Helpers: /xdrivers offline installs and pkg
###############################################################################

# Function: install_from_xdrivers
# Description: Installs a package from /xdrivers offline repository (live CD)
# Parameters:
#   $1 - Package name
# Returns: 0 on success, 1 if not available or failed
# Usage: if install_from_xdrivers "nvidia-driver"; then ...
install_from_xdrivers() {
    pkgname=$1
    log INFO "Installing $pkgname from offline package"
    if ${PKG} add -r XDrivers "$pkgname"; then
        return 0
    fi
    log WARN "Offline install of $pkgname failed"
    return 1
}


###############################################################################
# X server detection: Xorg vs XLibre
###############################################################################

# Function: detect_x_server
# Description: Detects which X server implementation is installed
# Parameters: None
# Returns: Prints "xlibre", "xorg", or "unknown" to stdout
# Usage: server=$(detect_x_server)
detect_x_server() {
    # Returns: "xlibre", "xorg", or "unknown"
    x_server="unknown"

    if command -v Xorg >/dev/null 2>&1; then
        version_output=$(Xorg -version 2>&1)
        if echo "$version_output" | grep -q "XLibre X Server"; then
            x_server="xlibre"
        elif echo "$version_output" | grep -q "X.Org X Server"; then
            x_server="xorg"
        fi
    fi

    echo "$x_server"
}

###############################################################################
# Hardware detection helpers
###############################################################################

# Function: detect_vm_guest
# Description: Returns the virtualization platform type from sysctl
# Parameters: None
# Returns: Prints kern.vm_guest value or "none" to stdout
# Usage: guest=$(detect_vm_guest)
detect_vm_guest() {
    # Print kern.vm_guest if available
    if sysctl -n kern.vm_guest >/dev/null 2>&1; then
        sysctl -n kern.vm_guest
        return 0
    fi
    echo "none"
    return 0
}

# Function: detect_virtualbox
# Description: Detects if running as a VirtualBox guest
# Parameters: None
# Returns: 0 if VirtualBox detected, 1 otherwise
# Usage: if detect_virtualbox; then ...
detect_virtualbox() {
    guest=$(detect_vm_guest)
    if echo "$guest" | grep -qi "vbox"; then
        return 0
    fi
    if dmesg 2>/dev/null | grep -qi "VirtualBox"; then
        return 0
    fi
    return 1
}

# Function: detect_vmware
# Description: Detects if running as a VMware guest
# Parameters: None
# Returns: 0 if VMware detected, 1 otherwise
# Usage: if detect_vmware; then ...
detect_vmware() {
    guest=$(detect_vm_guest)
    if echo "$guest" | grep -qi "vmware"; then
        return 0
    fi
    if dmesg 2>/dev/null | grep -qi "VMware"; then
        return 0
    fi
    return 1
}

# Function: detect_qemu
# Description: Detects if running as a QEMU/KVM guest
# Parameters: None
# Returns: 0 if QEMU/KVM detected, 1 otherwise
# Usage: if detect_qemu; then ...
detect_qemu() {
    guest=$(detect_vm_guest)
    if echo "$guest" | grep -qi "kvm"; then
        return 0
    fi
    if echo "$guest" | grep -qi "qemu"; then
        return 0
    fi
    return 1
}

# Function: detect_hyperv
# Description: Detects if running as a Hyper-V guest
# Parameters: None
# Returns: 0 if Hyper-V detected, 1 otherwise
# Usage: if detect_hyperv; then ...
detect_hyperv() {
    guest=$(detect_vm_guest)
    if echo "$guest" | grep -qi "hyperv"; then
        return 0
    fi
    if dmesg 2>/dev/null | grep -qi "Hyper-V"; then
        return 0
    fi
    return 1
}

# Function: detect_bhyve
# Description: Detects if running as a bhyve guest
# Parameters: None
# Returns: 0 if bhyve detected, 1 otherwise
# Usage: if detect_bhyve; then ...
detect_bhyve() {
    guest=$(detect_vm_guest)
    if echo "$guest" | grep -qi "bhyve"; then
        return 0
    fi
    # Secondary heuristic if needed
    if dmesg 2>/dev/null | grep -qi "bhyve"; then
        return 0
    fi
    return 1
}

# Function: detect_intel_gpu
# Description: Detects if an Intel GPU is present
# Parameters: None
# Returns: 0 if Intel GPU detected, 1 otherwise
# Usage: if detect_intel_gpu; then ...
detect_intel_gpu() {
    if pciconf -lv 2>/dev/null | grep -B4 -E "VGA" | grep -qi "Intel"; then
        return 0
    fi
    return 1
}

# Function: detect_amd_gpu
# Description: Detects if an AMD/ATI GPU is present
# Parameters: None
# Returns: 0 if AMD/ATI GPU detected, 1 otherwise
# Usage: if detect_amd_gpu; then ...
detect_amd_gpu() {
    if pciconf -lv 2>/dev/null | grep -B4 -E "VGA" | grep -Eq "AMD|ATI"; then
        return 0
    fi
    return 1
}

# Function: detect_nvidia_gpu
# Description: Detects if an NVIDIA GPU is present
# Parameters: None
# Returns: 0 if NVIDIA GPU detected, 1 otherwise
# Usage: if detect_nvidia_gpu; then ...
detect_nvidia_gpu() {
    if pciconf -lv 2>/dev/null | grep -B4 -E "VGA" | grep -qi "NVIDIA"; then
        return 0
    fi
    return 1
}

###############################################################################
# DRM Kernel Module Management
###############################################################################

# Function: load_drm_module
# Description: Loads a DRM kernel module immediately
# Parameters:
#   $1 - Module path (e.g., "/boot/modules/i915kms.ko")
# Returns: None (logs warning if module already loaded or fails)
# Usage: load_drm_module "/boot/modules/i915kms.ko"
load_drm_module() {
    module=$1
    log INFO "Loading DRM module: $module"
    kldload "$module" 2>/dev/null || log WARN "Module $module already loaded or failed to load"
}

# Function: add_to_kld_list
# Description: Adds a kernel module to rc.conf kld_list for auto-loading at boot
# Parameters:
#   $1 - Module path
# Returns: None
# Usage: add_to_kld_list "/boot/modules/i915kms.ko"
add_to_kld_list() {
    module=$1
    if grep -q "kld_list.*$module" /etc/rc.conf 2>/dev/null; then
        log INFO "$module already in kld_list"
    else
        log INFO "Adding $module to kld_list"
        sysrc -f /etc/rc.conf kld_list+="$module"
    fi
}

# Function: remove_from_kld_list
# Description: Removes a kernel module from rc.conf kld_list
# Parameters:
#   $1 - Module path
# Returns: None
# Usage: remove_from_kld_list "/boot/modules/amdgpu.ko"
remove_from_kld_list() {
    module=$1
    if grep -q "kld_list.*$module" /etc/rc.conf 2>/dev/null; then
        log INFO "Removing $module from kld_list"
        sysrc -f /etc/rc.conf kld_list-="$module"
    fi
}

# Function: detect_radeon_device
# Description: Detects if AMD GPU is an older card requiring radeonkms driver
# Parameters: None
# Returns: 0 if legacy Radeon device, 1 if modern AMD GPU
# Usage: if detect_radeon_device; then setup_radeonkms_drm; else setup_amdgpu_drm; fi
detect_radeon_device() {
    # Check if AMD GPU matches RADEON_DEVICE list (older AMD cards that need radeonkms)
    if pciconf -lv 2>/dev/null | grep -B 4 VGA | grep -q -E "($RADEON_DEVICE)"; then
        return 0  # Radeon device found - use radeonkms
    fi
    return 1  # Not a radeon device - use amdgpu
}

###############################################################################
# NVIDIA xorg.conf generation helper
###############################################################################

# Function: generate_nvidia_xorg_conf
# Description: Generates NVIDIA xorg.conf using X -configure and patches it
# Parameters: None
# Returns: 0 on success, exits on failure
# Usage: generate_nvidia_xorg_conf
generate_nvidia_xorg_conf() {
    # Generate xorg.conf for NVIDIA using X -configure
    # This replaces generic drivers (nv, scfb, vesa) with nvidia driver
    log INFO "Generating NVIDIA xorg.conf with X -configure"

    # X -configure requires X server to be stopped
    if pgrep -x Xorg >/dev/null 2>&1 || pgrep -x X >/dev/null 2>&1; then
        log ERROR "Cannot run X -configure while X server is running"
        log ERROR "Please shutdown to single-user mode, then run xconfig:"
        log ERROR "  1. Run: shutdown now"
        log ERROR "  2. Run: xconfig"
        log ERROR "  3. Run: reboot"
        exit 1
    fi

    X -configure 2>&1 | tee -a "$LOG_FILE"
    if [ -f /root/xorg.conf.new ]; then
        log INFO "Replacing generic drivers with nvidia driver"
        sed -i "" 's/"nv"/"nvidia"/g' /root/xorg.conf.new
        sed -i "" 's/scfb/nvidia/g' /root/xorg.conf.new
        sed -i "" 's/vesa/nvidia/g' /root/xorg.conf.new

        backup_xorg_conf
        mkdir -p /etc/X11
        cp /root/xorg.conf.new /etc/X11/xorg.conf
        log INFO "NVIDIA xorg.conf generated and installed"
        return 0
    else
        log ERROR "X -configure failed to create /root/xorg.conf.new"
        exit 1
    fi
}

###############################################################################
# Setup functions - DRM (Modern)
###############################################################################

# Function: cleanup_nvidia_config
# Description: Removes NVIDIA configuration when switching to DRM drivers
# Parameters: None
# Returns: None
# Usage: cleanup_nvidia_config
cleanup_nvidia_config() {
    # Remove NVIDIA boot config if present (used when switching from NVIDIA to DRM)
    if sysrc -n kldload_nvidia >/dev/null 2>&1; then
        log INFO "Removing kldload_nvidia from rc.conf"
        sysrc -x kldload_nvidia
    fi
    # Unload NVIDIA modules if loaded
    if kldstat | grep -q nvidia-modeset; then
        log INFO "Unloading nvidia-modeset module"
        kldunload nvidia-modeset 2>/dev/null || log WARN "Failed to unload nvidia-modeset"
    fi
    if kldstat | grep -q nvidia; then
        log INFO "Unloading nvidia module"
        kldunload nvidia 2>/dev/null || log WARN "Failed to unload nvidia"
    fi
}

# Function: setup_intel_drm
# Description: Configures Intel graphics using modern DRM (i915kms kernel module)
# Parameters: None
# Returns: None
# Usage: setup_intel_drm
setup_intel_drm() {
    log INFO "Setting up Intel DRM (i915kms)"
    cleanup_nvidia_config
    load_drm_module "/boot/modules/i915kms.ko"
    add_to_kld_list "/boot/modules/i915kms.ko"
    remove_from_kld_list "/boot/modules/amdgpu.ko"
    remove_from_kld_list "/boot/modules/radeonkms.ko"
    backup_xorg_conf
    rm -f "$XORG_CONF"
    log INFO "Intel DRM configured - modesetting driver will auto-detect"
}

# Function: setup_amdgpu_drm
# Description: Configures modern AMD graphics using DRM (amdgpu kernel module)
# Parameters: None
# Returns: None
# Usage: setup_amdgpu_drm
setup_amdgpu_drm() {
    log INFO "Setting up AMD DRM (amdgpu - modern cards)"
    cleanup_nvidia_config
    load_drm_module "/boot/modules/amdgpu.ko"
    add_to_kld_list "/boot/modules/amdgpu.ko"
    remove_from_kld_list "/boot/modules/radeonkms.ko"
    remove_from_kld_list "/boot/modules/i915kms.ko"
    backup_xorg_conf
    rm -f "$XORG_CONF"
    log INFO "amdgpu DRM configured - modesetting driver will auto-detect"
}

# Function: setup_radeonkms_drm
# Description: Configures older AMD graphics using DRM (radeonkms kernel module)
# Parameters: None
# Returns: None
# Usage: setup_radeonkms_drm
setup_radeonkms_drm() {
    log INFO "Setting up AMD DRM (radeonkms - older AMD cards)"
    cleanup_nvidia_config
    load_drm_module "/boot/modules/radeonkms.ko"
    add_to_kld_list "/boot/modules/radeonkms.ko"
    remove_from_kld_list "/boot/modules/amdgpu.ko"
    remove_from_kld_list "/boot/modules/i915kms.ko"
    backup_xorg_conf
    rm -f "$XORG_CONF"
    log INFO "radeonkms DRM configured - modesetting driver will auto-detect"
}

###############################################################################
# Setup functions - Legacy (Old Xorg Drivers)
###############################################################################

# Function: setup_intel_legacy
# Description: Configures Intel graphics using legacy xf86-video-intel driver
# Parameters: None
# Returns: None (exits on failure)
# Usage: setup_intel_legacy
setup_intel_legacy() {
    log INFO "Setting up Intel Legacy (old xorg driver)"
    if apply_config_template "XF86Config.intel"; then
        log INFO "Intel legacy configuration applied"
    else
        log ERROR "Failed to apply Intel legacy configuration"
        exit 1
    fi
}

# Function: setup_amdgpu_legacy
# Description: Configures AMD graphics using legacy xf86-video-amdgpu driver
# Parameters: None
# Returns: None (exits on failure)
# Usage: setup_amdgpu_legacy
setup_amdgpu_legacy() {
    log INFO "Setting up AMD Legacy (xf86-video-amdgpu driver)"
    if apply_config_template "XF86Config.amdgpu"; then
        log INFO "AMD amdgpu legacy configuration applied"
    else
        log ERROR "Failed to apply AMD amdgpu legacy configuration"
        exit 1
    fi
}

# Function: setup_ati_legacy
# Description: Configures ATI/AMD graphics using legacy xf86-video-ati driver
# Parameters: None
# Returns: None (exits on failure)
# Usage: setup_ati_legacy
setup_ati_legacy() {
    log INFO "Setting up ATI Legacy (xf86-video-ati driver)"
    if apply_config_template "XF86Config.ati"; then
        log INFO "ATI legacy configuration applied"
    else
        log ERROR "Failed to apply ATI legacy configuration"
        exit 1
    fi
}

###############################################################################
# NVIDIA Setup Functions
###############################################################################

# Function: detect_nvidia_driver_version
# Description: Determines the appropriate NVIDIA driver version based on detected GPU
# Parameters: None (reads NVIDIA GPU info from pciconf)
# Returns: Outputs driver version (latest, 580, 470, 390, 340) to stdout
# Usage: version=$(detect_nvidia_driver_version)
detect_nvidia_driver_version() {
    # Check which NVIDIA driver version is needed based on detected card
    # Check from newest to oldest to prevent newer cards matching legacy drivers
    if pciconf -lv | grep -B 3 VGA | grep -q -E "($NVIDIA_LATEST_CARDS)"; then
        echo "latest"
        return 0
    fi

    if pciconf -lv | grep -B 3 VGA | grep -q -E "($NVIDIA_580_CARDS)"; then
        echo "580"
        return 0
    fi

    if pciconf -lv | grep -B 3 VGA | grep -q -E "($NVIDIA_470_CARDS)"; then
        echo "470"
        return 0
    fi

    if pciconf -lv | grep -B 3 VGA | grep -q -E "($NVIDIA_390_CARDS)"; then
        echo "390"
        return 0
    fi

    if pciconf -lv | grep -B 3 VGA | grep -q -E "($NVIDIA_340_CARDS)"; then
        echo "340"
        return 0
    fi

    # Default to latest driver if no match found
    echo "latest"
    return 0
}

# Function: setup_nvidia_driver
# Description: Installs and configures NVIDIA driver for specified version
# Parameters:
#   $1 - Driver version (latest, 580, 470, 390, 340)
# Returns: None (exits on failure)
# Usage: setup_nvidia_driver "470"
setup_nvidia_driver() {
    version=$1
    server=$(detect_x_server)

    # Determine package name
    if [ "$version" = "latest" ]; then
        if [ "$server" = "xlibre" ]; then
            pkgname="xlibre-nvidia-driver"
        else
            pkgname="nvidia-driver"
        fi
    else
        if [ "$server" = "xlibre" ]; then
            pkgname="xlibre-nvidia-driver-${version}"
        else
            pkgname="nvidia-driver-${version}"
        fi
    fi

    # Install driver - prefer /xdrivers (live CD), fallback to pkg
    log INFO "Installing NVIDIA driver: $pkgname"
    if ! install_from_xdrivers "$pkgname"; then
        log INFO "Installing from pkg repositories"
        ${PKG} install -y "$pkgname" || log WARN "NVIDIA driver installation failed"
    fi

    # Determine which kernel module to load
    # Drivers 340 and 304 use "nvidia" module, not "nvidia-modeset"
    if [ "$version" = "340" ]; then
        log INFO "Loading nvidia kernel module (legacy driver)"
        kldload nvidia 2>/dev/null || log WARN "nvidia already loaded or failed"
        sysrc -f /etc/rc.conf kldload_nvidia="nvidia"
    else
        log INFO "Loading nvidia-modeset kernel module"
        kldload nvidia-modeset 2>/dev/null || log WARN "nvidia-modeset already loaded or failed"
        sysrc -f /etc/rc.conf kldload_nvidia="nvidia-modeset"
    fi

    # Generate NVIDIA xorg.conf
    generate_nvidia_xorg_conf
    log INFO "NVIDIA driver configured"
}

# Function: setup_nvidia
# Description: Configures NVIDIA latest driver (wrapper for setup_nvidia_driver)
# Parameters: None
# Returns: None
# Usage: setup_nvidia
setup_nvidia() {
    log INFO "Setting up NVIDIA Latest"
    setup_nvidia_driver "latest"
}

# Function: setup_nvidia_580
# Description: Configures NVIDIA 580 driver (wrapper for setup_nvidia_driver)
# Parameters: None
# Returns: None
# Usage: setup_nvidia_580
setup_nvidia_580() {
    log INFO "Setting up NVIDIA driver 580"
    setup_nvidia_driver "580"
}

# Function: setup_nvidia_470
# Description: Configures NVIDIA 470 driver (wrapper for setup_nvidia_driver)
# Parameters: None
# Returns: None
# Usage: setup_nvidia_470
setup_nvidia_470() {
    log INFO "Setting up NVIDIA driver 470"
    setup_nvidia_driver "470"
}

# Function: setup_nvidia_390
# Description: Configures NVIDIA 390 driver (wrapper for setup_nvidia_driver)
# Parameters: None
# Returns: None
# Usage: setup_nvidia_390
setup_nvidia_390() {
    log INFO "Setting up NVIDIA driver 390"
    setup_nvidia_driver "390"
}

# Function: setup_nvidia_340
# Description: Configures NVIDIA 340 driver (wrapper for setup_nvidia_driver)
# Parameters: None
# Returns: None
# Usage: setup_nvidia_340
setup_nvidia_340() {
    log INFO "Setting up NVIDIA driver 340"
    setup_nvidia_driver "340"
}


###############################################################################
# Setup functions - Virtualization & Fallback
###############################################################################

# Function: setup_virtualbox
# Description: Configures VirtualBox guest additions and X11 for VirtualBox VMs
# Parameters: None
# Returns: None
# Usage: setup_virtualbox
setup_virtualbox() {
    log INFO "Setting up VirtualBox environment"

    # Enable required rc.conf settings
    enable_rc_conf "vboxguest_enable" "YES"
    enable_rc_conf "vboxservice_enable" "YES"

    # Start services
    start_service_safe "vboxguest"
    start_service_safe "vboxservice"

    # Remove xorg.conf to allow auto-detection
    backup_xorg_conf
    rm -f "$XORG_CONF"

    log INFO "VirtualBox configured - driver will auto-detect"
}

# Function: setup_vmware
# Description: Configures X11 for VMware guest VMs
# Parameters: None
# Returns: None
# Usage: setup_vmware
setup_vmware() {
    log INFO "Setting up VMware guest"

    enable_rc_conf "vmware_guestd_enable" "YES"
    start_service_safe "vmware_guestd"

    apply_config_template "XF86Config.vmware" || log WARN "Failed to apply VMware template"
    log INFO "VMware configured"
}

###############################################################################
# Remove functions - remove unused VM packages
###############################################################################

remove_virtualbox() {
    log INFO "Adding VirtualBox guest packages to remove list"
    TO_REMOVE="${TO_REMOVE}	*virtualbox-ose-additions*"
}

remove_vmware() {
    log INFO "Adding VMware guest packages to remove list"
    TO_REMOVE="${TO_REMOVE}	*xf86-video-vmware*"
    TO_CLEAN_REMOVE="${TO_CLEAN_REMOVE}	*open-vm-tools*"
}

remove_qemu() {
    log INFO "Adding QEMU guest packages to remove list"
    TO_REMOVE="${TO_REMOVE}	*xf86-video-qxl*"
}

remove_drm() {
    log INFO "Adding DRM packages to clean remove list"
    TO_CLEAN_REMOVE="${TO_CLEAN_REMOVE}	*drm-kmod*"
    TO_CLEAN_REMOVE="${TO_CLEAN_REMOVE}	*gpu-firmware-amd-kmod*"
    TO_CLEAN_REMOVE="${TO_CLEAN_REMOVE}	*gpu-firmware-intel-kmod*"
    TO_CLEAN_REMOVE="${TO_CLEAN_REMOVE}	*gpu-firmware-radeon-kmod*"
}

remove_xdrivers() {
    if [ "${TO_REMOVE_XDRIVERS}" = "false" ]; then
        return 0
    fi
    if [ -d /xdrivers ]; then
        log INFO "Removing /xdrivers"
        rm -rf /xdrivers
    fi

    rm -f '/usr/local/etc/pkg/repo/XDrivers.conf' &&
    log INFO "Removing XDrivers repo config" || true
}

remove_fb() {
    log INFO "Adding framebuffer driver packages to remove list"
    TO_REMOVE="${TO_REMOVE}	*xf86-video-scfb*"
    TO_REMOVE="${TO_REMOVE}	*xf86-video-vesa*"
}

remove_commit() {
    if [ -z "${TO_REMOVE}" ]; then
        return 0
    fi
    log INFO "Removing unnecessary drivers"
    ${PKG} delete -yg $TO_REMOVE 2>/dev/null || true
}

clean_remove_commit() {
    # Skipping this step if we haven't been invoked with --clean.
    if [ "${TO_CLEAN}" = "false" ]; then
        return 0
    fi
    # Also skipping this step if we don't have any thing to remove.
    if [ -z "${TO_CLEAN_REMOVE}" ]; then
        return 0
    fi
    log INFO "Removing all unnecessary packages"
    ${PKG} delete -yg $TO_CLEAN_REMOVE 2>/dev/null || true
}

# Function: setup_fb
# Description: Configures generic framebuffer X11 drivers
# Parameters: None
# Returns: None
# Usage: setup_fb
setup_fb() {
    log INFO "Setting up framebuffer configuration, (no configuration needed)"
    backup_xorg_conf
    rm -f "$XORG_CONF"

    # Removing all other drivers than can be matched.
    remove_virtualbox
    remove_vmware
    remove_qemu
}

# Function: setup_dual
# Description: Configures dual monitor X11 setup
# Parameters: None
# Returns: None
# Usage: setup_dual
setup_dual() {
    log INFO "Setting up dual monitor configuration"
    apply_config_template "XF86Config.dual"
}

###############################################################################
# Dialog helpers (bsddialog preferred)
###############################################################################

# Function: have_bsddialog
# Description: Checks if bsddialog command is available
# Parameters: None
# Returns: 0 if available, 1 otherwise
# Usage: if have_bsddialog; then ...
have_bsddialog() {
    command -v bsddialog >/dev/null 2>&1
}

# Function: have_dialog
# Description: Checks if dialog command is available
# Parameters: None
# Returns: 0 if available, 1 otherwise
# Usage: if have_dialog; then ...
have_dialog() {
    command -v dialog >/dev/null 2>&1
}

# Function: interactive_menu
# Description: Displays an interactive menu using bsddialog or dialog
# Parameters:
#   $1 - Menu title
#   $@ - Menu items (alternating keys and descriptions)
# Returns: Selected menu item key to stdout, 0 on success, 1 on cancel
# Usage: choice=$(interactive_menu "Title" "key1" "desc1" "key2" "desc2")
interactive_menu() {
    title=$1
    shift

    if have_bsddialog; then
        bsddialog --title "$title" --menu "Select an option:" 0 0 0 "$@" 2>&1 1>/dev/tty
        return $?
    fi

    if have_dialog; then
        dialog --title "$title" --menu "Select an option:" 0 0 0 "$@" 2> /tmp/xconfig-menu.$$ 1>/dev/tty
        rc=$?
        choice=$(cat /tmp/xconfig-menu.$$ 2>/dev/null || true)
        rm -f /tmp/xconfig-menu.$$ || true
        if [ $rc -ne 0 ]; then
            return $rc
        fi
        printf '%s\n' "$choice"
        return 0
    fi

    log ERROR "Neither bsddialog nor dialog is available for interactive mode"
    exit 1
}

# Function: run_interactive
# Description: Displays interactive menu and executes selected configuration option
# Parameters: None
# Returns: 0 on success, 1 on cancel/error
# Usage: run_interactive
run_interactive() {
    # Build menu entries
    set -- \
        "auto"           "Automatic detection and configuration" \
        "intel-drm"      "Intel DRM (i915kms kernel module)" \
        "intel-legacy"   "Intel Legacy (xf86-video-intel)" \
        "amdgpu-drm"     "AMD DRM (amdgpu kernel module)" \
        "radeon-drm"     "Radeon DRM (radeonkms kernel module)" \
        "amdgpu-legacy"  "AMD Legacy (xf86-video-amdgpu)" \
        "ati-legacy"     "ATI Legacy (xf86-video-ati)" \
        "nvidia"         "NVIDIA Latest" \
        "nvidia-580"     "NVIDIA driver 580" \
        "nvidia-470"     "NVIDIA driver 470" \
        "nvidia-390"     "NVIDIA driver 390" \
        "nvidia-340"     "NVIDIA driver 340" \
        "virtualbox"     "VirtualBox Guest" \
        "vmware"         "VMware Guest" \
        "fb"             "Framebuffer" \
        "quit"           "Exit without changes"
        #"dual"           "Dual monitor template" \

    choice=$(interactive_menu "xconfig" "$@") || return 1

    case $choice in
        auto)            cmd_auto ;;
        intel-drm)       setup_intel_drm ;;
        intel-legacy)    setup_intel_legacy ;;
        amdgpu-drm)      setup_amdgpu_drm ;;
        radeon-drm)      setup_radeonkms_drm ;;
        amdgpu-legacy)   setup_amdgpu_legacy ;;
        ati-legacy)      setup_ati_legacy ;;
        nvidia)          setup_nvidia ;;
        nvidia-580)      setup_nvidia_580 ;;
        nvidia-470)      setup_nvidia_470 ;;
        nvidia-390)      setup_nvidia_390 ;;
        nvidia-340)      setup_nvidia_340 ;;
        virtualbox)      setup_virtualbox ;;
        vmware)          setup_vmware ;;
        fb)              setup_fb ;;
        #dual)            setup_dual ;;  This does not seems to be a finished feature.
        quit)            log INFO "User requested quit. No changes made." ;;
        *)               log ERROR "Unknown selection $choice" ;;
    esac
}

###############################################################################
# Auto mode logic
###############################################################################

# Function: cmd_auto
# Description: Performs automatic hardware detection and applies optimal X11 configuration
# Parameters: None
# Returns: None
# Usage: cmd_auto
cmd_auto() {
    log INFO "Running automatic detection"

    log INFO "Detected X server: $(detect_x_server)"

    # Hypervisors first
    if detect_virtualbox; then
        log INFO "Detected VirtualBox guest"
        remove_vmware
        remove_qemu
        remove_drm
        remove_fb
        setup_virtualbox
        return 0
    fi
    remove_virtualbox

    if detect_vmware; then
        log INFO "Detected VMware guest"
        remove_qemu
        remove_drm
        remove_fb
        setup_vmware
        return 0
    fi
    remove_vmware

    # Just remove the QXL driver, because it is broken.
    remove_qemu
    if detect_qemu; then
        log INFO "Detected QEMU/KVM guest"
        remove_drm
        setup_fb
        return 0
    fi

    if detect_hyperv; then
        log INFO "Detected Hyper-V guest"
        remove_drm
        setup_fb
        return 0
    fi

    if detect_bhyve; then
        log INFO "Detected bhyve guest"
        remove_drm
        setup_fb
        return 0
    fi

    # Physical GPUs - Intel and AMD use DRM, NVIDIA does not
    if detect_intel_gpu; then
        log INFO "Detected Intel GPU"
        remove_fb
        setup_intel_drm
        return 0
    fi

    if detect_amd_gpu; then
        log INFO "Detected AMD GPU"
        remove_fb
        if detect_radeon_device; then
            log INFO "Using radeonkms for older AMD card (RADEON_DEVICE match)"
            setup_radeonkms_drm
        else
            log INFO "Using amdgpu for modern AMD card"
            setup_amdgpu_drm
        fi
        return 0
    fi
    remove_drm

    if detect_nvidia_gpu; then
        log INFO "Detected NVIDIA GPU"
        remove_fb
        nvidia_version=$(detect_nvidia_driver_version)
        log INFO "NVIDIA driver version needed: $nvidia_version"
        setup_nvidia_driver "$nvidia_version"
        return 0
    fi

    # Fallback
    log WARN "Could not identify a supported GPU or hypervisor. Falling back to framebuffer drivers"
    setup_fb
}

###############################################################################
# Debug
###############################################################################

# Function: cmd_debug
# Description: Displays comprehensive system diagnostics for troubleshooting X11 configuration
# Parameters: None
# Returns: None
# Usage: cmd_debug
cmd_debug() {
    echo "===== xconfig debug ====="
    echo "Script:          $SCRIPT_NAME"
    echo "Script dir:      $SCRIPT_DIR"
    echo "Config dirs:     $LOCAL_CONFIG_DIR, $SYSTEM_CONFIG_DIR"
    echo "Xorg conf:       $XORG_CONF"
    echo "Backup dir:      $BACKUP_DIR"
    echo

    echo "FreeBSD version:"
    freebsd-version -kru 2>/dev/null || uname -a
    echo

    echo "kern.vm_guest:"
    detect_vm_guest
    echo

    echo "Detected GPUs (pciconf):"
    pciconf -lv 2>/dev/null | grep -B4 -E "VGA" || echo "No VGA/display controllers found"
    echo

    echo "X server detection:"
    detect_x_server
    echo

    echo "Available templates:"
    CONFIG_DIR=$(find_config_dir || true)
    if [ -n "$CONFIG_DIR" ]; then
        ls -1 "$CONFIG_DIR"
    else
        echo "No cardDetect directory found"
    fi
    echo

    if ${PKG} repositories XDrivers > /dev/null 2>&1
    then
        echo "XDrivers repository exists"
        ${PKG} update -fqr XDrivers
        ${PKG} rquery -ar XDrivers "%n %v" || echo "No packages found in repository"
    else
        echo "XDrivers repository does not exist"
    fi

    echo "===== End of debug ====="
}

###############################################################################
# Usage
###############################################################################

# Function: usage
# Description: Displays help message with all available commands and examples
# Parameters: None
# Returns: None
# Usage: usage
usage() {
    cat <<EOF
Usage: $SCRIPT_NAME <command>

Commands:
  auto             Automatic detection and configuration (prefers DRM)
  setup            Interactive setup menu (same as manual)
  manual           Interactive setup using bsddialog or dialog
  debug            Print diagnostic information
  help             Show this help message

Intel Graphics:
  intel-drm        Intel DRM (modern - i915kms kernel module, no xorg.conf)
  intel-legacy     Intel Legacy (old xf86-video-intel driver with xorg.conf)

AMD/ATI Graphics (DRM - Modern):
  amdgpu-drm       AMD DRM force amdgpu (modern cards, no xorg.conf)
  radeon-drm       AMD DRM force radeonkms (older AMD cards, no xorg.conf)

AMD/ATI Graphics (Legacy - Old Xorg drivers):
  amdgpu-legacy    AMD Legacy amdgpu (xf86-video-amdgpu with xorg.conf)
  ati-legacy       AMD Legacy ati (xf86-video-ati with xorg.conf)

NVIDIA Graphics:
  nvidia           NVIDIA Latest (driver 595)
  nvidia-580       NVIDIA driver 580
  nvidia-470       NVIDIA driver 470
  nvidia-390       NVIDIA driver 390
  nvidia-340       NVIDIA driver 340

Virtualization:
  virtualbox       VirtualBox guest (vboxguest/vboxservice modules)
  vmware           VMware guest configuration

Fallback/Recovery:
  fb               Generic framebuffer configuration

Flags:
  -c --clean clean Cleans everything unnecessary
  -h --help help   Shows this message

Examples:
  # Automatic detection (recommended)
  sudo $SCRIPT_NAME auto

  # Intel DRM (modern approach - no xorg.conf)
  sudo $SCRIPT_NAME intel-drm

  # AMD DRM for older cards
  sudo $SCRIPT_NAME radeon-drm

  # NVIDIA latest driver
  sudo $SCRIPT_NAME nvidia

  # NVIDIA legacy driver for older cards
  sudo $SCRIPT_NAME nvidia-470

  # VirtualBox guest
  sudo $SCRIPT_NAME virtualbox

  # Interactive menu
  sudo $SCRIPT_NAME setup

EOF
}

###############################################################################
# Main
###############################################################################

# Function: main
# Description: Script entry point that parses command-line arguments and dispatches to appropriate handlers
# Parameters:
#   $@ - Command-line arguments (command name and options)
# Returns: None (exits with status code)
# Usage: main "$@"
main() {
    # Check for root privileges
    if [ "$(id -u)" -ne 0 ]; then
        log ERROR "This script must be run as root (use sudo)"
        exit 1
    fi

    cmd="${1:-auto}"

    # Check if we need to do a full clean up.
    case "$cmd" in
        clean|-c|--clean)
            TO_CLEAN="true"
            # Only remove XDrivers if invoked with --clean.
            TO_REMOVE_XDRIVERS="true"
            shift
            cmd=${1:-auto}
            ;;
    esac


    case "$cmd" in
        auto)
            cmd_auto
            ;;
        setup|manual|interactive)
            run_interactive
            ;;
        intel-drm)
            setup_intel_drm
            ;;
        intel-legacy)
            setup_intel_legacy
            ;;
        amdgpu-drm)
            setup_amdgpu_drm
            ;;
        radeon-drm)
            setup_radeonkms_drm
            ;;
        amdgpu-legacy)
            setup_amdgpu_legacy
            ;;
        ati-legacy)
            setup_ati_legacy
            ;;
        nvidia)
            setup_nvidia
            ;;
        nvidia-580)
            setup_nvidia_580
            ;;
        nvidia-470)
            setup_nvidia_470
            ;;
        nvidia-390)
            setup_nvidia_390
            ;;
        nvidia-340)
            setup_nvidia_340
            ;;
        virtualbox)
            setup_virtualbox
            ;;
        vmware)
            setup_vmware
            ;;
        fb)
            setup_fb
            ;;
# Seems to be an unfinished feature
#        dual)
#            setup_dual
#            ;;
        debug)
            TO_REMOVE_XDRIVERS="false"
            cmd_debug
            ;;
        help|-h|--help)
            TO_REMOVE_XDRIVERS="false"
            usage
            ;;
        *)
            log ERROR "Unknown command: $cmd"
            usage
            exit 1
            ;;
    esac
}

main "$@"
remove_commit
clean_remove_commit
remove_xdrivers
