#!/bin/sh
# Parch Configuration Tool (parch-config)
# Based on parch-config https://github.com/RPi-Distro/parch-config
#
# See LICENSE file for copyright and license details
# shellcheck disable=SC2120

INTERACTIVE="${INTERACTIVE:-True}"
ASK_TO_REBOOT=0
BLACKLIST=/etc/modprobe.d/raspi-blacklist.conf

detect_platform() {
    if [ -f /proc/version ] && grep -qi microsoft /proc/version; then
        echo "wsl"
    elif [ "$(uname -m)" = "aarch64" ] && [ -f /proc/device-tree/model ]; then
        if grep -qi "raspberry" /proc/device-tree/model 2>/dev/null; then
            echo "rpi"
        else
            echo "aarch64"
        fi
    elif [ "$(uname -m)" = "x86_64" ]; then
        echo "x86_64"
    elif [ "$(uname -m)" = "aarch64" ]; then
        echo "aarch64"
    else
        echo "unknown"
    fi
}

PLATFORM=$(detect_platform)

is_wsl() { [ "$PLATFORM" = "wsl" ]; }
is_x86_64() { [ "$PLATFORM" = "x86_64" ]; }
is_rpi() { [ "$PLATFORM" = "rpi" ]; }
is_aarch64_board() { [ "$PLATFORM" = "rpi" ] || [ "$PLATFORM" = "aarch64" ]; }

if [ -e /boot/firmware/config.txt ] ; then
  FIRMWARE=/firmware
else
  FIRMWARE=
fi
CONFIG=/boot${FIRMWARE}/config.txt

USER=${SUDO_USER:-$(who -m | awk '{ print $1 }')}
if [ -z "$USER" ] && [ -n "$HOME" ]; then
  USER=$(getent passwd | awk -F: "\$6 == \"$HOME\" {print \$1}")
fi
if [ -z "$USER" ] || [ "$USER" = "root" ]; then
  USER=$(getent passwd | awk -F: '$3 == "1000" {print $1}')
fi

INIT="$(ps --no-headers -o comm 1)"

HOMEDIR="$(getent passwd "$USER" | cut -d: -f6)"
WAYFIRE_FILE="$HOMEDIR/.config/wayfire.ini"
LABWCENV_FILE="$HOMEDIR/.config/labwc/environment"
LABWCAST_FILE="$HOMEDIR/.config/labwc/autostart"

is_pi () {
  is_rpi
}

is_64bit () {
  [ "$(uname -m)" = "x86_64" ] || [ "$(uname -m)" = "aarch64" ]
}

if is_pi ; then
  if [ -e /proc/device-tree/chosen/os_prefix ]; then
    PREFIX="$(tr -d '\0' < /proc/device-tree/chosen/os_prefix)"
  fi
  CMDLINE="/boot${FIRMWARE}/${PREFIX}cmdline.txt"
else
  CMDLINE=/proc/cmdline
fi

# tests for Pi 1, 2 and 0 all test for specific boards...

is_pione() {
  if grep -q "^Revision\s*:\s*00[0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo; then
    return 0
  elif grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]0[0-36][0-9a-fA-F]$" /proc/cpuinfo ; then
    return 0
  else
    return 1
  fi
}

is_pitwo() {
  grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]04[0-9a-fA-F]$" /proc/cpuinfo
  return $?
}

is_pizero() {
  grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]0[9cC][0-9a-fA-F]$" /proc/cpuinfo
  return $?
}

# ...while tests for Pi 3 and 4 just test processor type, so will also find CM3, CM4, Zero 2 etc.

is_pithree() {
  grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F]2[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo
  return $?
}

is_pifour() {
  grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F]3[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo
  return $?
}

is_pifive() {
  grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F]4[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo
  return $?
}

is_cmfive() {
  grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]1[8aA][0-9a-fA-F]$" /proc/cpuinfo
  return $?
}

is_pi500() {
  grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]19[0-9a-fA-F]$" /proc/cpuinfo
  return $?
}

get_pi_type() {
  if is_pione; then
    echo 1
  elif is_pitwo; then
    echo 2
  elif is_pithree; then
    echo 3
  elif is_pifour; then
    echo 4
  elif is_pifive; then
    echo 5
  elif is_pizero; then
    echo 0
  else
    echo -1
  fi
}

gpu_has_mmu() {
  if is_pifour || is_pifive ; then
    return 0
  else
    return 1
  fi
}

is_live() {
  grep -q "boot=live" $CMDLINE
  return $?
}

is_ssh() {
  if pstree -p | grep -qE ".*sshd.*\($$\)"; then
    return 0
  else
    return 1
  fi
}

is_kms() {
    return 0
}

is_pulseaudio() {
  pgrep pulseaudio > /dev/null || pgrep pipewire-pulse > /dev/null
  return $?
}

is_wayfire() {
  pgrep wayfire > /dev/null
  return $?
}

is_labwc() {
  pgrep labwc > /dev/null
  return $?
}

is_wayland() {
  if is_wayfire; then
    return 0
  elif is_labwc; then
    return 0
  else
    return 1
  fi
}

has_analog() {
  if [ $(get_leds) -eq -1 ] ; then
    return 0
  else
    return 1
  fi
}

is_installed() {
  if pacman -Qi "$1" > /dev/null 2>&1; then
    return 0
  else
    return 1
  fi
}

get_package_version() {
  pacman -Qi "$1" 2>/dev/null | grep "^Version" | awk '{print $3}'
}

compare_versions() {
    ver1="$1"
    op="$2"
    ver2="$3"
    if [ -z "$ver1" ] || [ -z "$ver2" ]; then
        return 1
    fi
    case "$op" in
        lt)
            [ "$(echo -e "$ver1\n$ver2" | sort -V | head -n1)" = "$ver1" ] && [ "$ver1" != "$ver2" ]
            ;;
        le)
            [ "$(echo -e "$ver1\n$ver2" | sort -V | head -n1)" = "$ver1" ]
            ;;
        gt)
            [ "$(echo -e "$ver1\n$ver2" | sort -V | head -n1)" = "$ver2" ] && [ "$ver1" != "$ver2" ]
            ;;
        ge)
            [ "$(echo -e "$ver1\n$ver2" | sort -V | head -n1)" = "$ver2" ]
            ;;
        *)
            return 1
            ;;
    esac
}

can_configure() {
  if ! is_pi; then
    return 0
  fi
  if [ ! -e /etc/init.d/lightdm ] && [ ! -e /usr/lib/systemd/system/lightdm.service ]; then
    return 1
  fi
  if [ ! -e /boot${FIRMWARE}/start_x.elf ]; then
    return 1
  fi
  if [ -e $CONFIG ] && grep -q "^device_tree=$" $CONFIG; then
    return 1
  fi
  if ! mountpoint -q /boot${FIRMWARE}; then
    return 1
  fi
  if [ ! -e $CONFIG ]; then
    touch $CONFIG
  fi
  return 0
}

calc_wt_size() {
  # NOTE: it's tempting to redirect stderr to /dev/null, so supress error
  # output from tput. However in this case, tput detects neither stdout or
  # stderr is a tty and so only gives default 80, 24 values
  WT_HEIGHT=18
  WT_WIDTH=$(tput cols)

  if [ -z "$WT_WIDTH" ] || [ "$WT_WIDTH" -lt 60 ]; then
    WT_WIDTH=80
  fi
  if [ "$WT_WIDTH" -gt 178 ]; then
    WT_WIDTH=120
  fi
  WT_MENU_HEIGHT=$((WT_HEIGHT - 7))
}

do_about() {
  whiptail --msgbox "\
Parch Configuration Tool (parch-config)
Based on parch-config by Raspberry Pi Foundation

This tool provides a straightforward way of initial
configuration of your Parch Linux system.

Platform: $PLATFORM
$(is_installed parch-config && echo "Version: $(get_package_version parch-config)" || echo "Version: development")\
" 20 70 1
  return 0
}

get_can_expand() {
  ROOT_PART="$(findmnt / -o source -n)"
  ROOT_DEV="/dev/$(lsblk -no pkname "$ROOT_PART")"

  PART_NUM="$(echo "$ROOT_PART" | grep -o "[[:digit:]]*$")"

  if [ "$PART_NUM" -ne 2 ]; then
    echo 1
    exit
  fi

  LAST_PART_NUM=$(parted "$ROOT_DEV" -ms unit s p | tail -n 1 | cut -f 1 -d:)
  if [ "$LAST_PART_NUM" -ne "$PART_NUM" ]; then
    echo 1
    exit
  fi
  echo 0
}

do_expand_rootfs() {
  ROOT_PART="$(findmnt / -o source -n)"
  ROOT_DEV="/dev/$(lsblk -no pkname "$ROOT_PART")"

  PART_NUM="$(echo "$ROOT_PART" | grep -o "[[:digit:]]*$")"

  LAST_PART_NUM=$(parted "$ROOT_DEV" -ms unit s p | tail -n 1 | cut -f 1 -d:)
  if [ "$LAST_PART_NUM" -ne "$PART_NUM" ]; then
    whiptail --msgbox "$ROOT_PART is not the last partition. Don't know how to expand" 20 60 2
    return 0
  fi

  # Get the starting offset of the root partition
  PART_START=$(parted "$ROOT_DEV" -ms unit s p | grep "^${PART_NUM}" | cut -f 2 -d: | sed 's/[^0-9]//g')
  [ "$PART_START" ] || return 1
  # Return value will likely be error for fdisk as it fails to reload the
  # partition table because the root fs is mounted
  fdisk "$ROOT_DEV" <<EOF
p
d
$PART_NUM
n
p
$PART_NUM
$PART_START

p
w
EOF
  ASK_TO_REBOOT=1

  # now set up an init.d script
cat <<EOF > /etc/init.d/resize2fs_once &&
#!/bin/sh
### BEGIN INIT INFO
# Provides:          resize2fs_once
# Required-Start:
# Required-Stop:
# Default-Start: 3
# Default-Stop:
# Short-Description: Resize the root filesystem to fill partition
# Description:
### END INIT INFO

. /lib/lsb/init-functions

case "\$1" in
  start)
    log_daemon_msg "Starting resize2fs_once" &&
    resize2fs "$ROOT_PART" &&
    update-rc.d resize2fs_once remove &&
    rm /etc/init.d/resize2fs_once &&
    log_end_msg \$?
    ;;
  *)
    echo "Usage: \$0 start" >&2
    exit 3
    ;;
esac
EOF
  chmod +x /etc/init.d/resize2fs_once &&
  if [ -d /etc/systemd/system ]; then
    cat << EOF > /etc/systemd/system/resize2fs_once.service
[Unit]
Description=Resize root filesystem
After=local-fs-pre.target
DefaultDependencies=no

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/resize2fs $ROOT_PART
ExecStart=/bin/systemctl disable resize2fs_once.service

[Install]
WantedBy=multi-user.target
EOF
    systemctl enable resize2fs_once.service
  else
    update-rc.d resize2fs_once defaults
  fi &&
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Root partition has been resized.\nThe filesystem will be enlarged upon the next reboot" 20 60 2
  fi
}

set_config_var() {
  awk -v key="$1" -v value="$2" '
    $0 ~ "^#?[[:space:]]*" key "=" {
      print key "=" value
      made_change=1
      next
    }
    { print }
    END { if (!made_change) print key "=" value }
  ' "$3" > "$3.bak" && mv "$3.bak" "$3"
}

clear_config_var() {
  sed -i "s/^\([[:space:]]*$1=\)/#\1/" "$2"
}

get_config_var() {
  awk -v key="$1" '
    $0 ~ "^[[:space:]]*" key "=" {
      sub("^[[:space:]]*" key "=", "")
      print
      found=1
      exit
    }
    END { if (!found) print 0 }
  ' "$2"
}

ensure_user_conf_dir() {
  if [ ! -d "$HOMEDIR/.config" ]; then
    install -d -o "$USER" -g "$USER" "$HOMEDIR/.config"
  fi
}

get_overscan() {
  OVS=$(get_config_var disable_overscan $CONFIG)
  if [ $OVS -eq 1 ]; then
    echo 1
  else
    echo 0
  fi
}

do_overscan() {
  DEFAULT=--defaultno
  CURRENT=0
  if [ $(get_overscan) -eq 0 ]; then
    DEFAULT=
    CURRENT=1
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like to enable compensation for displays with overscan?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq $CURRENT ]; then
    ASK_TO_REBOOT=1
  fi
  if [ $RET -eq 0 ] ; then
    set_config_var disable_overscan 0 $CONFIG
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    sed $CONFIG -i -e "s/^overscan_/#overscan_/"
    set_config_var disable_overscan 1 $CONFIG
    STATUS=disabled
  else
    return $RET
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Display overscan compensation is $STATUS" 20 60 1
  fi
}

get_overscan_kms() {
  RES=$(grep "HDMI-$1" /usr/share/ovscsetup.sh 2> /dev/null | grep margin | rev | cut -d ' ' -f 1 | rev)
  if [ -z $RES ] ; then
    echo 1
  elif [ $RES -eq 0 ] ; then
    echo 1
  else
    echo 0
  fi
}

do_overscan_kms() {
  if [ "$INTERACTIVE" = True ]; then
    NDEVS=$(xrandr -q | grep -c connected)
    if [ $NDEVS -gt 1 ] ; then
      DEV=$(whiptail --menu "Select the output for which overscan compensation is to be set" 20 60 10 "1" "HDMI-1" "2" "HDMI-2" 3>&1 1>&2 2>&3)
      if [ $? -eq 1 ] ; then
        return
      fi
    else
      DEV=1
    fi
    if [ $(get_overscan_kms $DEV) -eq 1 ]; then
      DEFAULT=--defaultno
    else
      DEFAULT=
    fi
    if whiptail --yesno "Would you like to enable overscan compensation for HDMI-$DEV?" $DEFAULT 20 60 2 ; then
      PIX=16
      STATUS="enabled"
    else
      PIX=0
      STATUS="disabled"
    fi
  else
    DEV=$1
    if [ $2 -eq 1 ] ; then
      PIX=0
    else
      PIX=16
    fi
  fi
  xrandr --output HDMI-$DEV --set "left margin" $PIX --set "right margin" $PIX --set "top margin" $PIX --set "bottom margin" $PIX
  # hack to force reload when not using mutter
  if ! pgrep mutter > /dev/null ; then
    xrandr --output HDMI-$DEV --reflect x
    xrandr --output HDMI-$DEV --reflect normal
  fi
  sed $CONFIG -i -e "s/^overscan_/#overscan_/"
  set_config_var disable_overscan 1 $CONFIG
  if [ -e /usr/share/ovscsetup.sh ] ; then
    if grep "HDMI-$DEV" /usr/share/ovscsetup.sh 2> /dev/null | grep -q margin ; then
      sed /usr/share/ovscsetup.sh -i -e "s/xrandr --output HDMI-$DEV.*margin.*/xrandr --output HDMI-$DEV --set \"left margin\" $PIX --set \"right margin\" $PIX --set \"top margin\" $PIX --set \"bottom margin\" $PIX/"
    else
      echo "xrandr --output HDMI-$DEV --set \"left margin\" $PIX --set \"right margin\" $PIX --set \"top margin\" $PIX --set \"bottom margin\" $PIX" >> /usr/share/ovscsetup.sh
    fi
  else
    echo "#!/bin/sh" > /usr/share/ovscsetup.sh
    echo "xrandr --output HDMI-$DEV --set \"left margin\" $PIX --set \"right margin\" $PIX --set \"top margin\" $PIX --set \"bottom margin\" $PIX" >> /usr/share/ovscsetup.sh
  fi
  if ! grep -q ovscsetup /usr/share/dispsetup.sh 2> /dev/null ; then
    sed /usr/share/dispsetup.sh -i -e "s#exit#if [ -e /usr/share/ovscsetup.sh ] ; then\n. /usr/share/ovscsetup.sh\nfi\nexit#"
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Display overscan compensation for HDMI-$DEV is $STATUS" 20 60 1
  fi
}

get_blanking() {
  if is_wayfire; then
    if ! grep -q dpms_timeout $WAYFIRE_FILE ; then
      echo 1
    elif ! grep -q "dpms_timeout.*-1" $WAYFIRE_FILE ; then
      echo 0
    else
      echo 1
    fi
  elif is_labwc; then
    if [ -e $LABWCAST_FILE ] && grep -q swayidle $LABWCAST_FILE ; then
      echo 0
    else
      echo 1
    fi
  else
    if ! [ -f "/etc/X11/xorg.conf.d/10-blanking.conf" ]; then
      echo 0
    else
      echo 1
    fi
  fi
}

do_blanking() {
  DEFAULT=--defaultno
  CURRENT=0
  if [ "$(get_blanking)" -eq 0 ]; then
    DEFAULT=
    CURRENT=1
  fi
  if is_wayfire; then
    if [ "$INTERACTIVE" = True ]; then
      whiptail --yesno "Would you like to enable screen blanking?" $DEFAULT 20 60 2
      RET=$?
    else
      RET=$1
    fi
    if [ "$RET" -eq 0 ] ; then
      if grep -q dpms_timeout $WAYFIRE_FILE ; then
        sed -i 's/dpms_timeout.*/dpms_timeout=600/' $WAYFIRE_FILE
      else
        if grep -q "\[idle\]" $WAYFIRE_FILE ; then
          sed -i 's/\[idle]/[idle]\ndpms_timeout=600/' $WAYFIRE_FILE
        else
          echo '\n[idle]\ndpms_timeout=600' >> $WAYFIRE_FILE
          chown $USER:$USER $WAYFIRE_FILE
        fi
      fi
      STATUS=enabled
    elif [ "$RET" -eq 1 ]; then
      if grep -q dpms_timeout $WAYFIRE_FILE ; then
        sed -i 's/dpms_timeout.*/dpms_timeout=-1/' $WAYFIRE_FILE
      else
        if grep -q "\[idle\]" $WAYFIRE_FILE ; then
          sed -i 's/\[idle]/[idle]\ndpms_timeout=-1/' $WAYFIRE_FILE
        else
          echo '\n[idle]\ndpms_timeout=-1' >> $WAYFIRE_FILE
          chown $USER:$USER $WAYFIRE_FILE
        fi
      fi
      STATUS=disabled
    else
      return "$RET"
    fi
  elif is_labwc; then
    if [ "$INTERACTIVE" = True ]; then
      whiptail --yesno "Would you like to enable screen blanking?" $DEFAULT 20 60 2
      RET=$?
    else
      RET=$1
    fi
    if [ "$RET" -eq "$CURRENT" ]; then
      ASK_TO_REBOOT=1
    fi
    ensure_user_conf_dir
    mkdir -p "$HOMEDIR/.config/labwc/"
    chown -R $USER:$USER "$HOMEDIR/.config/labwc/"
    if [ "$RET" -eq 0 ] ; then
      echo "swayidle -w timeout 600 'wlopm --off \\*' resume 'wlopm --on \\*' &" >> $LABWCAST_FILE
      chown $USER:$USER $LABWCAST_FILE
      echo "swayidle -w timeout 600 'wlopm --off \\*' resume 'wlopm --on \\*' &" >> /etc/xdg/labwc-greeter/autostart
      STATUS=enabled
    elif [ "$RET" -eq 1 ]; then
      if [ -e $LABWCAST_FILE ] ; then
        sed -i '/swayidle/d' $LABWCAST_FILE
      fi
      if [ -e /etc/xdg/labwc-greeter/autostart ] ; then
        sed -i '/swayidle/d' /etc/xdg/labwc-greeter/autostart
      fi
      STATUS=disabled
    else
      return "$RET"
    fi
  else
    if [ "$INTERACTIVE" = True ]; then
      if is_installed xscreensaver; then
        whiptail --msgbox "Warning: xscreensaver is installed and may override parch-config settings" 20 60 2
      fi
      whiptail --yesno "Would you like to enable screen blanking?" $DEFAULT 20 60 2
      RET=$?
    else
      RET=$1
    fi
    if [ "$RET" -eq "$CURRENT" ]; then
      ASK_TO_REBOOT=1
    fi
    rm -f /etc/X11/xorg.conf.d/10-blanking.conf
    sed -i '/^\o033/d' /etc/issue
    if [ "$RET" -eq 0 ] ; then
      STATUS=enabled
    elif [ "$RET" -eq 1 ]; then
      mkdir -p /etc/X11/xorg.conf.d/
      cp /usr/share/parch-config/10-blanking.conf /etc/X11/xorg.conf.d/
      printf "\\033[9;0]" >> /etc/issue
      STATUS=disabled
    else
      return "$RET"
    fi
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Screen blanking is $STATUS" 20 60 1
  fi
}

do_change_pass() {
  whiptail --msgbox "You will now be asked to enter a new password for the $USER user" 20 60 1
  passwd $USER &&
  whiptail --msgbox "Password changed successfully" 20 60 1
}

update_wayfire_keyboard() {
  if ! is_wayfire ; then
    return
  fi
  MODEL=$(grep XKBMODEL /etc/default/keyboard | cut -d= -f2 | tr -d '"')
  LAYOUT=$(grep XKBLAYOUT /etc/default/keyboard | cut -d= -f2 | tr -d '"')
  VARIANT=$(grep XKBVARIANT /etc/default/keyboard | cut -d= -f2 | tr -d '"')
  OPTIONS=$(grep XKBOPTIONS /etc/default/keyboard | cut -d= -f2 | tr -d '"')
  UFILE=$WAYFIRE_FILE
  if [ ! -e $UFILE ] && [ -e /etc/wayfire/template.ini ] ; then
    CONFIG_DIR="$(dirname "$UFILE")"
    if ! [ -d "$CONFIG_DIR" ]; then
      install -o "$USER" -g "$USER" -d "$CONFIG_DIR"
    fi
    cp /etc/wayfire/template.ini $UFILE
    chown $USER:$USER $UFILE
  fi
  if [ -e $UFILE ] ; then
    grep -q "\\[input\\]" $UFILE || printf "\n[input]" >> $UFILE
    if grep -q xkb_model $UFILE ; then sed -i s/xkb_model.*/xkb_model=$MODEL/ $UFILE ; else sed -i s/\\[input\\]/[input]\\nxkb_model=$MODEL/ $UFILE ; fi
    if grep -q xkb_layout $UFILE ; then sed -i s/xkb_layout.*/xkb_layout=$LAYOUT/ $UFILE ; else sed -i s/\\[input\\]/[input]\\nxkb_layout=$LAYOUT/ $UFILE ; fi
    if grep -q xkb_variant $UFILE ; then sed -i s/xkb_variant.*/xkb_variant=$VARIANT/ $UFILE ; else sed -i s/\\[input\\]/[input]\\nxkb_variant=$VARIANT/ $UFILE ; fi
    if grep -q xkb_options $UFILE ; then sed -i s/xkb_options.*/xkb_options=$OPTIONS/ $UFILE ; else sed -i s/\\[input\\]/[input]\\nxkb_options=$OPTIONS/ $UFILE ; fi
  fi
  UFILE="/usr/share/greeter.ini"
  if [ ! -e $UFILE ] && [ -e /etc/wayfire/gtemplate.ini ] ; then
    cp /etc/wayfire/gtemplate.ini $UFILE
  fi
  if [ -e $UFILE ] ; then
    grep -q "\\[input\\]" $UFILE || printf "\n[input]" >> $UFILE
    if grep -q xkb_model $UFILE ; then sed -i s/xkb_model.*/xkb_model=$MODEL/ $UFILE ; else sed -i s/\\[input\\]/[input]\\nxkb_model=$MODEL/ $UFILE ; fi
    if grep -q xkb_layout $UFILE ; then sed -i s/xkb_layout.*/xkb_layout=$LAYOUT/ $UFILE ; else sed -i s/\\[input\\]/[input]\\nxkb_layout=$LAYOUT/ $UFILE ; fi
    if grep -q xkb_variant $UFILE ; then sed -i s/xkb_variant.*/xkb_variant=$VARIANT/ $UFILE ; else sed -i s/\\[input\\]/[input]\\nxkb_variant=$VARIANT/ $UFILE ; fi
    if grep -q xkb_options $UFILE ; then sed -i s/xkb_options.*/xkb_options=$OPTIONS/ $UFILE ; else sed -i s/\\[input\\]/[input]\\nxkb_options=$OPTIONS/ $UFILE ; fi
  fi
}

update_labwc_keyboard() {
  MODEL=$(grep XKBMODEL /etc/default/keyboard | cut -d= -f2 | tr -d '"')
  LAYOUT=$(grep XKBLAYOUT /etc/default/keyboard | cut -d= -f2 | tr -d '"')
  VARIANT=$(grep XKBVARIANT /etc/default/keyboard | cut -d= -f2 | tr -d '"')
  OPTIONS=$(grep XKBOPTIONS /etc/default/keyboard | cut -d= -f2 | tr -d '"')
  UFILE=$LABWCENV_FILE
  ensure_user_conf_dir
  mkdir -p "$HOMEDIR/.config/labwc/"
  chown -R $USER:$USER "$HOMEDIR/.config/labwc/"
  if [ -e $UFILE ] ; then
    if grep -q XKB_DEFAULT_MODEL $UFILE ; then sed -i s/XKB_DEFAULT_MODEL.*/XKB_DEFAULT_MODEL=$MODEL/ $UFILE ; else echo XKB_DEFAULT_MODEL=$MODEL >> $UFILE ; fi
    if grep -q XKB_DEFAULT_LAYOUT $UFILE ; then sed -i s/XKB_DEFAULT_LAYOUT.*/XKB_DEFAULT_LAYOUT=$LAYOUT/ $UFILE ; else echo XKB_DEFAULT_LAYOUT=$LAYOUT >> $UFILE ; fi
    if grep -q XKB_DEFAULT_VARIANT $UFILE ; then sed -i s/XKB_DEFAULT_VARIANT.*/XKB_DEFAULT_VARIANT=$VARIANT/ $UFILE ; else echo XKB_DEFAULT_VARIANT=$VARIANT >> $UFILE ; fi
    if grep -q XKB_DEFAULT_OPTIONS $UFILE ; then sed -i s/XKB_DEFAULT_OPTIONS.*/XKB_DEFAULT_OPTIONS=$OPTIONS/ $UFILE ; else echo XKB_DEFAULT_OPTIONS=$OPTIONS >> $UFILE ; fi
  else
    echo XKB_DEFAULT_MODEL=$MODEL >> $UFILE
    echo XKB_DEFAULT_LAYOUT=$LAYOUT >> $UFILE
    echo XKB_DEFAULT_VARIANT=$VARIANT >> $UFILE
    echo XKB_DEFAULT_OPTIONS=$OPTIONS >> $UFILE
  fi
  chown $USER:$USER $UFILE
  UFILE="/etc/xdg/labwc-greeter/environment"
  if [ ! -e $UFILE ] ; then
    UFILE="/usr/share/labwc/environment"
  fi
  if [ -e $UFILE ] ; then
    if grep -q XKB_DEFAULT_MODEL $UFILE ; then sed -i s/XKB_DEFAULT_MODEL.*/XKB_DEFAULT_MODEL=$MODEL/ $UFILE ; else echo XKB_DEFAULT_MODEL=$MODEL >> $UFILE ; fi
    if grep -q XKB_DEFAULT_LAYOUT $UFILE ; then sed -i s/XKB_DEFAULT_LAYOUT.*/XKB_DEFAULT_LAYOUT=$LAYOUT/ $UFILE ; else echo XKB_DEFAULT_LAYOUT=$LAYOUT >> $UFILE ; fi
    if grep -q XKB_DEFAULT_VARIANT $UFILE ; then sed -i s/XKB_DEFAULT_VARIANT.*/XKB_DEFAULT_VARIANT=$VARIANT/ $UFILE ; else echo XKB_DEFAULT_VARIANT=$VARIANT >> $UFILE ; fi
    if grep -q XKB_DEFAULT_OPTIONS $UFILE ; then sed -i s/XKB_DEFAULT_OPTIONS.*/XKB_DEFAULT_OPTIONS=$OPTIONS/ $UFILE ; else echo XKB_DEFAULT_OPTIONS=$OPTIONS >> $UFILE ; fi
  fi
  if is_labwc ; then
    kill -HUP `pgrep -x labwc`  # does the equivalent of labwc --reconfigure, but works as sudo...
  fi
}

update_squeekboard() {
  PREFIX=""
  if [ -n "$SUDO_USER" ] ; then
    PREFIX="sudo -u $USER XDG_RUNTIME_DIR=/run/user/$SUDO_UID DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$SUDO_UID/bus "
  fi
  LAYOUT1=$(grep XKBLAYOUT /etc/default/keyboard | cut -d= -f2 | tr -d '"' | cut -d, -f1)
  VARIANT1=$(grep XKBVARIANT /etc/default/keyboard | cut -d= -f2 | tr -d '"' | cut -d, -f1)
  LAYOUT2=$(grep XKBLAYOUT /etc/default/keyboard | cut -d= -f2 | tr -d '"' | cut -d, -f2 -s)
  VARIANT2=$(grep XKBVARIANT /etc/default/keyboard | cut -d= -f2 | tr -d '"' | cut -d, -f2 -s)
  GSET="[('xkb', '$LAYOUT1"
  if [ -z "$VARIANT1" ] ; then
    GSET=$GSET"')"
  else
    GSET=$GSET"+$VARIANT1')"
  fi
  if ! [ -z "$LAYOUT2" ] ; then
    GSET=$GSET", ('xkb', '$LAYOUT2"
    if [ -z "$VARIANT2" ] ; then
      GSET=$GSET"')"
    else
      GSET=$GSET"+$VARIANT2')"
    fi
  fi
  GSET=$GSET"]"
  if ! [ -e /etc/dconf/profile/user ] ; then
    mkdir -p "/etc/dconf/profile/"
    echo "user-db:user\nsystem-db:local" >> /etc/dconf/profile/user
  fi
  FILE=/etc/dconf/db/local.d/00_keyboard
  if [ -e $FILE ] ; then
    if grep -q "^sources" $FILE ; then
      sed $FILE -i -e "s/^sources=.*/sources=$GSET/"
    else
      if grep -q "\[org/gnome/desktop/input-sources\]" $FILE ; then
        sed $FILE -i -e "s#\[org/gnome/desktop/input-sources\]#\[org/gnome/desktop/input-sources\]\nsources=$GSET#" 
      else
        echo "[org/gnome/desktop/input-sources]\nsources=$GSET" >> $FILE
      fi
    fi
  else
    mkdir -p "/etc/dconf/db/local.d/"
    echo "[org/gnome/desktop/input-sources]\nsources=$GSET" > $FILE
  fi
  dconf update
  if [ "$1" = restart ] ; then
    if pgrep squeekboard > /dev/null ; then
      pkill squeekboard
      $PREFIX squeekboard > /dev/null 2> /dev/null &
    fi
  fi
}

do_configure_keyboard() {
  printf "Reloading keymap. This may take a short while\n"
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "For Arch Linux:\n\nUse localectl or edit /etc/vconsole.conf\n\nExample: localectl set-keymap us" 20 60 1
  else
    KEYMAP="$1"
    if command -v localectl >/dev/null 2>&1; then
      localectl set-keymap "$KEYMAP"
    fi
  fi
  update_wayfire_keyboard
  update_labwc_keyboard
  update_squeekboard restart
  if [ "$INIT" = "systemd" ]; then
    systemctl restart keyboard-setup 2>/dev/null || true
  fi
  udevadm trigger --subsystem-match=input --action=change
  return 0
}

do_change_keyboard_rc_gui () {
  grep -q XKBMODEL /etc/default/keyboard && sed -i "s/XKBMODEL=.*/XKBMODEL=\"$1\"/g" /etc/default/keyboard || echo "XKBMODEL=\"$1\"" >> /etc/default/keyboard
  grep -q XKBLAYOUT /etc/default/keyboard && sed -i "s/XKBLAYOUT=.*/XKBLAYOUT=\"$2\"/g" /etc/default/keyboard || echo "XKBLAYOUT=\"$2\"" >> /etc/default/keyboard
  grep -q XKBVARIANT /etc/default/keyboard && sed -i "s/XKBVARIANT=.*/XKBVARIANT=\"$3\"/g" /etc/default/keyboard || echo "XKBVARIANT=\"$3\"" >> /etc/default/keyboard
  grep -q XKBOPTIONS /etc/default/keyboard && sed -i "s/XKBOPTIONS=.*/XKBOPTIONS=\"$4\"/g" /etc/default/keyboard || echo "XKBOPTIONS=\"$4\"" >> /etc/default/keyboard
  update_wayfire_keyboard
  update_labwc_keyboard
  update_squeekboard restart
  if ! is_wayland ; then
    systemctl restart keyboard-setup 2>/dev/null || true
  fi
  setsid sh -c 'exec setupcon -k --force <> /dev/tty1 >&0 2>&1'
  if ! is_wayland ; then
    udevadm trigger --subsystem-match=input --action=change
    udevadm settle
    KBSTR="-model $1 -layout $2"
    if [ -n "$3" ] ; then
      KBSTR="$KBSTR -variant $3"
    fi
    if [ -n "$4" ] ; then
      KBSTR="$KBSTR -option -option $4"
    fi
    setxkbmap "$KBSTR"
  fi
}

do_change_locale() {
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "For Arch Linux:\n\n1. sudo nano /etc/locale.gen\n2. Uncomment your locale\n3. sudo locale-gen\n4. sudo localectl set-locale LANG=en_US.UTF-8" 20 60 1
  else
    if [ -n "$1" ]; then
      if command -v localectl >/dev/null 2>&1; then
        localectl set-locale "LANG=$1"
      fi
    fi
  fi
}

do_change_locale_rc_gui() {
  if [ -n "$1" ]; then
    if command -v localectl >/dev/null 2>&1; then
      localectl set-locale "LANG=$1"
    fi
  fi
}

do_change_timezone() {
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "For Arch Linux:\n\nUse: sudo timedatectl set-timezone America/New_York\n\nList timezones: timedatectl list-timezones" 20 60 1
  else
    if [ -n "$1" ] && [ -f "/usr/share/zoneinfo/$1" ]; then
      if command -v timedatectl >/dev/null 2>&1; then
        timedatectl set-timezone "$1"
      fi
    fi
  fi
}

do_change_timezone_rc_gui() {
  if [ -n "$1" ] && [ -f "/usr/share/zoneinfo/$1" ]; then
    if command -v timedatectl >/dev/null 2>&1; then
      timedatectl set-timezone "$1"
    fi
  fi
}

get_wifi_country() {
  CODE=${1:-0}
  if is_installed crda && [ -e /etc/default/crda ]; then
    . /etc/default/crda
  elif grep -q "cfg80211.ieee80211_regdom=" "$CMDLINE"; then
    REGDOMAIN="$(sed -n 's/.*cfg80211.ieee80211_regdom=\(\S*\).*/\1/p' "$CMDLINE")"
  elif systemctl -q is-active dhcpcd; then
    REGDOMAIN="$(wpa_cli get country | tail -n 1)"
  else
    REGDOMAIN="$(iw reg get | sed -n "0,/country/s/^country \(.\+\):.*$/\1/p")"
  fi
  if [ -z "$REGDOMAIN" ] \
     || ! grep -q "^${REGDOMAIN}[[:space:]]" /usr/share/zoneinfo/iso3166.tab; then
    return 1
  fi
  if [ "$CODE" = 0 ]; then
    echo "$REGDOMAIN"
  fi
  return 0
}

do_wifi_country() {
  if [ "$INTERACTIVE" = True ]; then
    value=$(hexdump -C /lib/firmware/regulatory.db | grep -oE '[A-Z]{2}' | sort -u | sed 's/^/^/;s/$/\t/' | grep -f- /usr/share/zoneinfo/iso3166.tab | tr '\t\n' '/')
    oIFS="$IFS"
    IFS="/"
    #shellcheck disable=2086
    REGDOMAIN=$(whiptail --menu "Select the country in which the Pi is to be used" 20 60 10 ${value} 3>&1 1>&2 2>&3)
    IFS="$oIFS"
  else
    REGDOMAIN=$1
  fi
  if ! grep -q "^${REGDOMAIN}[[:space:]]" /usr/share/zoneinfo/iso3166.tab; then
    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "$REGDOMAIN is not a valid ISO/IEC 3166-1 alpha2 code" 20 60
    fi
    return 1
  fi
  sed -i \
    -e "s/\s*cfg80211.ieee80211_regdom=\S*//" \
    -e "s/\(.*\)/\1 cfg80211.ieee80211_regdom=$REGDOMAIN/" \
    "$CMDLINE"
  if is_installed crda && [ -e /etc/default/crda ]; then
    # This mechanism has been removed from Bookworm and should no longer be used
    rm -f /etc/default/crda
  fi
  if ! ischroot; then
    iw reg set "$REGDOMAIN"
  fi

  IFACE="$(list_wlan_interfaces | head -n 1)"
  if [ "$INIT" = "systemd" ] && [ -n "$IFACE" ] && systemctl -q is-active dhcpcd; then
    wpa_cli -i "$IFACE" set country "$REGDOMAIN" > /dev/null 2>&1
    wpa_cli -i "$IFACE" save_config > /dev/null 2>&1
  fi

  if [ "$INIT" = "systemd" ] && ! ischroot && systemctl -q is-active NetworkManager; then
    nmcli radio wifi on
  elif hash rfkill 2> /dev/null; then
    rfkill unblock wifi
    if [ -f /var/lib/NetworkManager/NetworkManager.state ]; then
      sed -i 's/^WirelessEnabled=.*/WirelessEnabled=true/' /var/lib/NetworkManager/NetworkManager.state
    fi
  fi
  if is_pi; then
    for filename in /var/lib/systemd/rfkill/*:wlan ; do
      if ! [ -e "$filename" ]; then
        continue
      fi
      echo 0 > "$filename"
    done
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Wireless LAN country set to $REGDOMAIN" 20 60 1
  fi
  if ! ischroot; then
    if pgrep -x wf-panel-pi > /dev/null; then
      wfpanelctl netman cset
    elif pgrep -x lxpanel-pi > /dev/null; then
      lxpanelctl-pi command netman cset
    elif pgrep -x lxpanel > /dev/null; then
      lxpanelctl command netman cset
    fi
  fi
}

get_hostname() {
  HOSTNAME_FILE="${MOCK_HOSTNAME_FILE:-/etc/hostname}"
  # skip comments and empty lines, return first valid hostname with whitespace stripped
  grep -v '^[[:space:]]*\(#\|$\)' "$HOSTNAME_FILE" | head -n1 | xargs
}

do_hostname() {
  HOSTNAME_FILE="${MOCK_HOSTNAME_FILE:-/etc/hostname}"
  HOSTS_FILE="${MOCK_HOSTS_FILE:-/etc/hosts}"

  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "\
Please note: RFCs mandate that a hostname's labels \
may contain only the ASCII letters 'a' through 'z' (case-insensitive),
the digits '0' through '9', and the hyphen.
Hostname labels cannot begin or end with a hyphen.
No other symbols, punctuation characters, or blank spaces are permitted.\
" 20 70 1
  fi
  CURRENT_HOSTNAME=$(get_hostname)
  if [ "$INTERACTIVE" = True ]; then
    NEW_HOSTNAME=$(whiptail --inputbox "Please enter a hostname" 20 60 "$CURRENT_HOSTNAME" 3>&1 1>&2 2>&3)
    RET=$?
  else
    NEW_HOSTNAME="$1"
    RET=0
  fi
  if [ $RET -eq 0 ]; then
    if [ "$INIT" != "systemd" ] || \
       ! systemctl -q is-active dbus || \
       ischroot || \
       ! hostnamectl hostname "$NEW_HOSTNAME" 2> /dev/null; then
      echo "$NEW_HOSTNAME" > "$HOSTNAME_FILE"
    fi
    if [ -f "$HOSTS_FILE" ]; then
      sed -i "s/^[[:space:]]*127\.0\.1\.1[[:space:]].*/127.0.1.1\t$NEW_HOSTNAME/" "$HOSTS_FILE"
    fi
    if [ "$CURRENT_HOSTNAME" != "$NEW_HOSTNAME" ]; then
      ASK_TO_REBOOT=1
    fi
  fi
}

do_overclock() {
  if ! is_pione && ! is_pitwo; then
    whiptail --msgbox "Only Pi 1 or Pi 2 can be overclocked with this tool." 20 60 2
    return 1
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "\
Be aware that overclocking may reduce the lifetime of your
Raspberry Pi. If overclocking at a certain level causes
system instability, try a more modest overclock. Hold down
shift during boot to temporarily disable overclock.
See https://www.raspberrypi.org/documentation/configuration/config-txt/overclocking.md for more information.\
" 20 70 1
  if is_pione; then
    OVERCLOCK=$(whiptail --menu "Choose overclock preset" 20 60 10 \
      "None" "700MHz ARM, 250MHz core, 400MHz SDRAM, 0 overvolt" \
      "Modest" "800MHz ARM, 250MHz core, 400MHz SDRAM, 0 overvolt" \
      "Medium" "900MHz ARM, 250MHz core, 450MHz SDRAM, 2 overvolt" \
      "High" "950MHz ARM, 250MHz core, 450MHz SDRAM, 6 overvolt" \
      "Turbo" "1000MHz ARM, 500MHz core, 600MHz SDRAM, 6 overvolt" \
      3>&1 1>&2 2>&3)
  elif is_pitwo; then
    OVERCLOCK=$(whiptail --menu "Choose overclock preset" 20 60 10 \
      "None" "900MHz ARM, 250MHz core, 450MHz SDRAM, 0 overvolt" \
      "High" "1000MHz ARM, 500MHz core, 500MHz SDRAM, 2 overvolt" \
      3>&1 1>&2 2>&3)
  fi
  else
    OVERCLOCK=$1
    true
  fi
  if [ $? -eq 0 ]; then
    case "$OVERCLOCK" in
      None)
        clear_overclock
        ;;
      Modest)
        set_overclock Modest 800 250 400 0
        ;;
      Medium)
        set_overclock Medium 900 250 450 2
        ;;
      High)
        if is_pione; then
          set_overclock High 950 250 450 6
        else
          set_overclock High 1000 500 500 2
        fi
        ;;
      Turbo)
        set_overclock Turbo 1000 500 600 6
        ;;
      *)
        whiptail --msgbox "Programmer error, unrecognised overclock preset" 20 60 2
        return 1
        ;;
    esac
    ASK_TO_REBOOT=1
  fi
}

set_overclock() {
  set_config_var arm_freq $2 $CONFIG &&
  set_config_var core_freq $3 $CONFIG &&
  set_config_var sdram_freq $4 $CONFIG &&
  set_config_var over_voltage $5 $CONFIG &&
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Set overclock to preset '$1'" 20 60 2
  fi
}

clear_overclock () {
  clear_config_var arm_freq $CONFIG &&
  clear_config_var core_freq $CONFIG &&
  clear_config_var sdram_freq $CONFIG &&
  clear_config_var over_voltage $CONFIG &&
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Set overclock to preset 'None'" 20 60 2
  fi
}

get_ssh() {
  if systemctl -q is-enabled ssh; then
    echo 0
  else
    echo 1
  fi
}

do_ssh() {
  DEFAULT=--defaultno
  if [ "$(get_ssh)" -eq 0 ]; then
    DEFAULT=
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno \
      "Would you like the SSH server to be enabled?\n\nCaution: Default and weak passwords are a security risk when SSH is enabled!" \
      $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ "$RET" -eq 0 ]; then
    ssh-keygen -A &&
    systemctl enable sshd &&
    systemctl start sshd &&
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    systemctl disable sshd &&
    systemctl stop sshd &&
    STATUS=disabled
  else
    return "$RET"
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The SSH server is $STATUS" 20 60 1
  fi
}

get_vnc() {
  if is_wayland; then
    if systemctl status wayvnc.service | grep -q -w active; then
      echo 0
    else
      echo 1
    fi
  else
    if systemctl status vncserver-x11-serviced.service | grep -q -w active; then
      echo 0
    else
      echo 1
    fi
  fi
}

do_vnc() {
  DEFAULT=--defaultno
  if [ $(get_vnc) -eq 0 ]; then
    DEFAULT=
  fi
  APT_GET_FLAGS=""
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like the VNC Server to be enabled?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
    APT_GET_FLAGS="-y"
  fi
  if [ $RET -eq 0 ]; then
    if is_installed wayvnc; then
      wayvnc_version="$(get_package_version wayvnc)"
      if compare_versions "$wayvnc_version" lt 0.8; then
        whiptail --msgbox "WayVNC version 0.8 or greater is required (have $wayvnc_version)" 20 60 1
        return 1
      fi

      systemctl stop wayvnc.service

      # In case wayvnc is already running via older xdg-autostart machanism
      if [ -e /etc/xdg/autostart/wayvnc.desktop ] ; then
        rm /etc/xdg/autostart/wayvnc.desktop
      fi
    fi
    if is_installed realvnc-vnc-server; then
      systemctl disable vncserver-x11-serviced.service
      systemctl stop vncserver-x11-serviced.service
    fi
    if is_wayland; then
      if is_installed wayvnc; then
        systemctl enable wayvnc.service &&
        systemctl start wayvnc.service &&
        STATUS=enabled
      else
        return 1
      fi
    else
      if is_installed realvnc-vnc-server || pacman -S --noconfirm realvnc-vnc-server 2>/dev/null; then
        systemctl enable vncserver-x11-serviced.service &&
        systemctl start vncserver-x11-serviced.service &&
        STATUS=enabled
      else
        return 1
      fi
    fi
  elif [ $RET -eq 1 ]; then
    if is_installed wayvnc; then
      systemctl disable wayvnc.service
      systemctl stop wayvnc.service
    fi
    if is_installed realvnc-vnc-server; then
      systemctl disable vncserver-x11-serviced.service
      systemctl stop vncserver-x11-serviced.service
    fi
    STATUS=disabled
  else
    return $RET
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The VNC Server is $STATUS" 20 60 1
  fi
}

get_rpi_connect() {
  PREFIX=""
  if [ -n "$SUDO_USER" ] ; then
    PREFIX="sudo -u $USER XDG_RUNTIME_DIR=/run/user/$SUDO_UID DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$SUDO_UID/bus "
  fi
  if $PREFIX systemctl --user -q status rpi-connect.service | grep -q -w active; then
    echo 0
  else
    echo 1
  fi
}

do_rpi_connect() {
  PREFIX=""
  if [ -n "$SUDO_USER" ] ; then
    PREFIX="sudo -u $USER XDG_RUNTIME_DIR=/run/user/$SUDO_UID DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$SUDO_UID/bus "
  fi
  APT_GET_FLAGS=""
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like to enable screen sharing over Raspberry Pi Connect?" --defaultno 20 60 2
    RET=$?
  else
    APT_GET_FLAGS="-y"
    RET=$1
  fi
  rpi_connect_version="$(get_package_version rpi-connect)"
  if [ $RET -eq 0 ]; then
    if is_installed rpi-connect || pacman -S --noconfirm "$APT_GET_FLAGS" rpi-connect; then
      if compare_versions "$rpi_connect_version" lt 1.3; then
        $PREFIX systemctl --user -q enable rpi-connect.service rpi-connect-wayvnc.service
        $PREFIX systemctl --user -q start rpi-connect.service rpi-connect-wayvnc.service
      elif compare_versions "$rpi_connect_version" lt 2.0; then
        $PREFIX systemctl --user -q enable rpi-connect.service rpi-connect-wayvnc.service rpi-connect-wayvnc-watcher.path
        $PREFIX systemctl --user -q start rpi-connect.service
      else
        $PREFIX rpi-connect on > /dev/null 2>&1
      fi
      STATUS="Screen sharing via Raspberry Pi Connect is enabled"
    else
      return 1
    fi
  elif [ $RET -eq 1 ]; then
    if [ "$INTERACTIVE" = True ]; then
      whiptail --yesno "Would you like to enable remote shell access over Raspberry Pi Connect?" --defaultno 20 60 2
      RET=$?
    fi
    if [ $RET -eq 0 ]; then
      if is_installed rpi-connect || is_installed rpi-connect-lite || pacman -S --noconfirm "$APT_GET_FLAGS" rpi-connect-lite; then
        if compare_versions "$rpi_connect_version" lt 2.0; then
          $PREFIX systemctl --user -q enable rpi-connect.service
          $PREFIX systemctl --user -q start rpi-connect.service
        else
          $PREFIX rpi-connect on > /dev/null 2>&1
        fi
        STATUS="Remote shell access via Raspberry Pi Connect is enabled"
      else
        return 1
      fi
    elif [ $RET -eq 1 ]; then
      if compare_versions "$rpi_connect_version" lt 1.3; then
        $PREFIX systemctl --user -q stop rpi-connect.service rpi-connect-wayvnc.service
        $PREFIX systemctl --user -q disable rpi-connect-wayvnc.service rpi-connect.service
      elif compare_versions "$rpi_connect_version" lt 2.0; then
        $PREFIX systemctl --user -q stop rpi-connect.service
        $PREFIX systemctl --user -q disable rpi-connect.service rpi-connect-wayvnc.service rpi-connect-wayvnc-watcher.path
      else
        $PREFIX rpi-connect off > /dev/null 2>&1
      fi
      STATUS="Raspberry Pi Connect is disabled"
    else
      return $RET
    fi
  else
    return $RET
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "$STATUS" 20 60 1
  fi
}

get_spi() {
  if grep -q -E "^(device_tree_param|dtparam)=([^,]*,)*spi(=(on|true|yes|1))?(,.*)?$" $CONFIG; then
    echo 0
  else
    echo 1
  fi
}

do_spi() {
  DEFAULT=--defaultno
  if [ $(get_spi) -eq 0 ]; then
    DEFAULT=
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like the SPI interface to be enabled?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq 0 ]; then
    SETTING=on
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    SETTING=off
    STATUS=disabled
  else
    return $RET
  fi

  set_config_var dtparam=spi $SETTING $CONFIG &&
  if ! [ -e $BLACKLIST ]; then
    touch $BLACKLIST
  fi
  sed $BLACKLIST -i -e "s/^\(blacklist[[:space:]]*spi[-_]bcm2708\)/#\1/"
  dtparam spi=$SETTING

  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The SPI interface is $STATUS" 20 60 1
  fi
}

get_i2c() {
  if grep -q -E "^(device_tree_param|dtparam)=([^,]*,)*i2c(_arm)?(=(on|true|yes|1))?(,.*)?$" $CONFIG; then
    echo 0
  else
    echo 1
  fi
}

do_i2c() {
  DEFAULT=--defaultno
  if [ $(get_i2c) -eq 0 ]; then
    DEFAULT=
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like the ARM I2C interface to be enabled?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq 0 ]; then
    SETTING=on
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    SETTING=off
    STATUS=disabled
  else
    return $RET
  fi

  set_config_var dtparam=i2c_arm $SETTING $CONFIG &&
  if ! [ -e $BLACKLIST ]; then
    touch $BLACKLIST
  fi
  sed $BLACKLIST -i -e "s/^\(blacklist[[:space:]]*i2c[-_]bcm2708\)/#\1/"
  sed /etc/modules -i -e "s/^#[[:space:]]*\(i2c[-_]dev\)/\1/"
  if ! grep -q "^i2c[-_]dev" /etc/modules; then
    printf "i2c-dev\n" >> /etc/modules
  fi
  dtparam i2c_arm=$SETTING
  modprobe i2c-dev

  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The ARM I2C interface is $STATUS" 20 60 1
  fi
}

get_serial_cons() {
  if grep -q -E "console=(serial0|ttyAMA0|ttyS0)" $CMDLINE ; then
    echo 0
  else
    echo 1
  fi
}

get_serial_hw() {
  if is_pifive ; then
    if grep -q -E "dtparam=uart0=off" $CONFIG ; then
      echo 1
    elif grep -q -E "dtparam=uart0" $CONFIG ; then
      echo 0
    else
      echo 1
    fi
  else
    if grep -q -E "^enable_uart=1" $CONFIG ; then
      echo 0
    elif grep -q -E "^enable_uart=0" $CONFIG ; then
      echo 1
    elif [ -e /dev/serial0 ] ; then
      echo 0
    else
      echo 1
    fi
  fi
}

do_serial_cons() {
  if [ $1 -eq 0 ] ; then
    if grep -q "console=ttyAMA0" $CMDLINE ; then
      if [ -e /proc/device-tree/aliases/serial0 ]; then
        sed -i $CMDLINE -e "s/console=ttyAMA0/console=serial0/"
      fi
    elif ! grep -q "console=ttyAMA0" $CMDLINE && ! grep -q "console=serial0" $CMDLINE ; then
      if [ -e /proc/device-tree/aliases/serial0 ]; then
        sed -i $CMDLINE -e "s/root=/console=serial0,115200 root=/"
      else
        sed -i $CMDLINE -e "s/root=/console=ttyAMA0,115200 root=/"
      fi
    fi
  else
    sed -i $CMDLINE -e "s/console=ttyAMA0,[0-9]\+ //"
    sed -i $CMDLINE -e "s/console=serial0,[0-9]\+ //"
  fi
}

do_serial_hw() {
  if [ $1 -eq 0 ] ; then
    if is_pifive ; then
      set_config_var dtparam=uart0 on $CONFIG
    else
      set_config_var enable_uart 1 $CONFIG
    fi
  else
    if is_pifive ; then
      sed $CONFIG -i -e "/dtparam=uart0.*/d"
    else
      set_config_var enable_uart 0 $CONFIG
    fi
  fi
}

do_serial() {
  DEFAULTS=--defaultno
  DEFAULTH=--defaultno
  CURRENTS=0
  CURRENTH=0
  if [ $(get_serial_cons) -eq 0 ]; then
    DEFAULTS=
    CURRENTS=1
  fi
  if [ $(get_serial_hw) -eq 0 ]; then
    DEFAULTH=
    CURRENTH=1
  fi
  whiptail --yesno "Would you like a login shell to be accessible over serial?" $DEFAULTS 20 60 2
  RET=$?
  if [ $RET -eq $CURRENTS ]; then
    ASK_TO_REBOOT=1
  fi
  if [ $RET -eq 0 ]; then
    do_serial_cons 0
    SSTATUS=enabled
    do_serial_hw 0
    HSTATUS=enabled
  elif [ $RET -eq 1 ]; then
    do_serial_cons 1
    SSTATUS=disabled
    whiptail --yesno "Would you like the serial port hardware to be enabled?" $DEFAULTH 20 60 2
    RET=$?
    if [ $RET -eq $CURRENTH ]; then
      ASK_TO_REBOOT=1
    fi
    if [ $RET -eq 0 ]; then
      do_serial_hw 0
      HSTATUS=enabled
    elif [ $RET -eq 1 ]; then
      do_serial_hw 1
      HSTATUS=disabled
    else
      return $RET
    fi
  else
    return $RET
  fi
  whiptail --msgbox "The serial login shell is $SSTATUS\nThe serial interface is $HSTATUS" 20 60 1
}

do_serial_pi5() {
  DEFAULTS=--defaultno
  DEFAULTH=--defaultno
  CURRENTS=0
  CURRENTH=0
  if [ $(get_serial_cons) -eq 0 ]; then
    DEFAULTS=
    CURRENTS=1
  fi
  if [ $(get_serial_hw) -eq 0 ]; then
    DEFAULTH=
    CURRENTH=1
  fi
  whiptail --yesno "Would you like a login shell to be accessible over serial?" $DEFAULTS 20 60 2
  RET=$?
  if [ $RET -eq $CURRENTS ]; then
    ASK_TO_REBOOT=1
  fi
  if [ $RET -eq 0 ]; then
    do_serial_cons 0
    SSTATUS=enabled
  elif [ $RET -eq 1 ]; then
    do_serial_cons 1
    SSTATUS=disabled
  else
    return $RET
  fi
  whiptail --yesno "Would you like the serial port hardware to be enabled?" $DEFAULTH 20 60 2
  RET=$?
  if [ $RET -eq $CURRENTH ]; then
    ASK_TO_REBOOT=1
  fi
  if [ $RET -eq 0 ]; then
    do_serial_hw 0
    HSTATUS=enabled
  elif [ $RET -eq 1 ]; then
    do_serial_hw 1
    HSTATUS=disabled
  else
    return $RET
  fi
  whiptail --msgbox "The serial login shell is $SSTATUS\nThe serial interface is $HSTATUS" 20 60 1
}

get_pci() {
  if grep -q -E "^dtparam=pciex1_gen=3$" $CONFIG; then
    echo 0
  else
    echo 1
  fi
}

do_pci() {
  DEFAULT=--defaultno
  CURRENT=1
  if [ $(get_pci) -eq 0 ]; then
    DEFAULT=
    CURRENT=0
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like PCIe Gen 3 to be enabled?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq 0 ]; then
    set_config_var dtparam=pciex1_gen 3 $CONFIG
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    clear_config_var dtparam=pciex1_gen $CONFIG
    STATUS=disabled
  else
    return $RET
  fi
  if [ $RET -ne $CURRENT ]; then
    ASK_TO_REBOOT=1
  fi

  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "PCIe Gen 3 is $STATUS" 20 60 1
  fi
}

disable_raspi_config_at_boot() {
  if [ -e /etc/profile.d/parch-config.sh ]; then
    rm -f /etc/profile.d/parch-config.sh
    if [ -e /etc/systemd/system/getty@tty1.service.d/parch-config-override.conf ]; then
      rm /etc/systemd/system/getty@tty1.service.d/parch-config-override.conf
    fi
    telinit q
  fi
}

get_boot_cli() {
  if [ "$(basename $(readlink -f /etc/systemd/system/default.target))" = graphical.target ] \
     && systemctl is-enabled lightdm > /dev/null 2>&1; then
    echo 1
  else
    echo 0
  fi
}

get_autologin() {
  if [ $(get_boot_cli) -eq 0 ]; then
    # booting to CLI
    if [ -e /etc/systemd/system/getty@tty1.service.d/autologin.conf ] ; then
      echo 0
    else
      echo 1
    fi
  else
    # booting to desktop - check the autologin for lightdm
    if grep -q "^autologin-user=" /etc/lightdm/lightdm.conf ; then
      echo 0
    else
      echo 1
    fi
  fi
}

get_autologin_cli() {
  if [ -e /etc/systemd/system/getty@tty1.service.d/autologin.conf ] ; then
    echo 0
  else
    echo 1
  fi
}

get_autologin_desktop() {
  if grep -q "^autologin-user=" /etc/lightdm/lightdm.conf ; then
    echo 0
  else
    echo 1
  fi
}

get_pi4video () {
  if grep -q "^hdmi_enable_4kp60=1" $CONFIG ; then
    echo 0
  else
    echo 1
  fi
}

do_pi4video() {
  CURRENT=$(get_pi4video)
  DEFAULT=--defaultno
  if [ $CURRENT -eq 0 ]; then
    DEFAULT=
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like to enable 4Kp60 output on HDMI0?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq 0 ]; then
    sed $CONFIG -i -e "s/^#\?hdmi_enable_4kp60=.*/hdmi_enable_4kp60=1/"
    if ! grep -q "hdmi_enable_4kp60" $CONFIG ; then
      sed $CONFIG -i -e "\$ahdmi_enable_4kp60=1"
    fi
    sed $CONFIG -i -e "s/^enable_tvout=/#enable_tvout=/"
    sed $CONFIG -i -e "s/^dtoverlay=vc4-kms-v3d.*/dtoverlay=vc4-kms-v3d/"
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    sed $CONFIG -i -e "s/^hdmi_enable_4kp60=/#hdmi_enable_4kp60=/"
    sed $CONFIG -i -e "s/^enable_tvout=/#enable_tvout=/"
    sed $CONFIG -i -e "s/^dtoverlay=vc4-kms-v3d.*/dtoverlay=vc4-kms-v3d/"
    STATUS=disabled
  else
    return $RET
  fi
  if [ $RET -ne $CURRENT ]; then
    ASK_TO_REBOOT=1
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "4Kp60 is $STATUS" 20 60 1
  fi
}

get_composite() {
  if grep -q "^dtoverlay=vc4-kms-v3d,composite" $CONFIG ; then
    echo 0
  else
    echo 1
  fi
}

do_composite() {
  CURRENT=$(get_composite)
  DEFAULT=--defaultno
  if [ $CURRENT -eq 0 ]; then
    DEFAULT=
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like composite video output to be enabled? Warning - this will disable the HDMI outputs." $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq 0 ]; then
    sed $CONFIG -i -e "s/^dtoverlay=vc4-kms-v3d.*/dtoverlay=vc4-kms-v3d,composite/"
    sed $CONFIG -i -e "s/^#\?enable_tvout=.*/enable_tvout=1/"
    sed $CONFIG -i -e "s/^hdmi_enable_4kp60=/#hdmi_enable_4kp60=/"
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    sed $CONFIG -i -e "s/^dtoverlay=vc4-kms-v3d.*/dtoverlay=vc4-kms-v3d/"
    sed $CONFIG -i -e "s/^enable_tvout=/#enable_tvout=/"
    STATUS=disabled
  else
    return $RET
  fi
  if [ $RET -ne $CURRENT ]; then
    ASK_TO_REBOOT=1
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Composite video output is $STATUS" 20 60 1
  fi
}

get_leds () {
  if [ ! -e /sys/class/leds/ACT/trigger ] ; then
    echo -1
  elif grep -q "\\[actpwr\\]" /sys/class/leds/ACT/trigger ; then
    echo 0
  elif grep -q "\\[default-on\\]" /sys/class/leds/ACT/trigger ; then
    echo 1
  else
    echo -1
  fi
}

do_leds() {
  CURRENT=$(get_leds)
  if [ $CURRENT -eq -1 ] ; then
    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "The LED behaviour cannot be changed on this model of Raspberry Pi" 20 60 1
    fi
    return 1
  fi
  DEFAULT=--defaultno
  if [ $CURRENT -eq 0 ]; then
    DEFAULT=
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like the power LED to flash during disk activity?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq 0 ]; then
    LEDSET="actpwr"
    STATUS="flash for disk activity"
  elif [ $RET -eq 1 ]; then
    LEDSET="default-on"
    STATUS="be on constantly"
  else
    return $RET
  fi
  sed $CONFIG -i -e "s/dtparam=act_led_trigger=.*/dtparam=act_led_trigger=$LEDSET/"
  if ! grep -q "dtparam=act_led_trigger" $CONFIG ; then
    sed $CONFIG -i -e "\$adtparam=act_led_trigger=$LEDSET"
  fi
  echo $LEDSET | tee /sys/class/leds/ACT/trigger > /dev/null
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The power LED will $STATUS" 20 60 1
  fi
}

get_fan() {
  if grep -q ^dtoverlay=gpio-fan $CONFIG ; then
    echo 0
  else
    echo 1
  fi
}

get_fan_gpio() {
  GPIO=$(grep ^dtoverlay=gpio-fan $CONFIG | cut -d, -f2 | cut -d= -f2)
  if [ -z $GPIO ]; then
    GPIO=14
  fi
  echo $GPIO
}

get_fan_temp() {
  TEMP=$(grep ^dtoverlay=gpio-fan $CONFIG | cut -d, -f3 | cut -d= -f2)
  if [ -z $TEMP ]; then
    TEMP=80000
  fi
  echo $((TEMP / 1000))
}

do_fan() {
  GNOW=$(get_fan_gpio)
  TNOW=$(get_fan_temp)
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like to enable fan temperature control?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq 0 ] ; then
    if [ "$INTERACTIVE" = True ]; then
      GPIO=$(whiptail --inputbox "To which GPIO is the fan connected?" 20 60 "$GNOW" 3>&1 1>&2 2>&3)
    else
      if [ -z $2 ]; then
        GPIO=14
      else
        GPIO=$2
      fi
    fi
    if [ $? -ne 0 ] ; then
      return 0
    fi
    if ! echo "$GPIO" | grep -q "^[[:digit:]]*$" ; then
      if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "GPIO must be a number between 2 and 27" 20 60 1
      fi
      return 1
    fi
    if [ "$GPIO" -lt 2 ] || [ "$GPIO" -gt 27 ]  ; then
      if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "GPIO must be a number between 2 and 27" 20 60 1
      fi
      return 1
    fi
    if [ "$INTERACTIVE" = True ]; then
      TIN=$(whiptail --inputbox "At what temperature in degrees Celsius should the fan turn on?" 20 60 "$TNOW" 3>&1 1>&2 2>&3)
    else
      if [ -z $3 ]; then
        TIN=80
      else
        TIN=$3
      fi
    fi
    if [ $? -ne 0 ] ; then
      return 0
    fi
    if ! echo "$TIN" | grep -q "^[[:digit:]]*$" ; then
      if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "Temperature must be a number between 60 and 120" 20 60 1
      fi
      return 1
    fi
    if [ "$TIN" -lt 60 ] || [ "$TIN" -gt 120 ]  ; then
      if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "Temperature must be a number between 60 and 120" 20 60 1
      fi
      return 1
    fi
    TEMP=$((TIN * 1000))
  fi
  if [ $RET -eq 0 ]; then
    if ! grep -q "dtoverlay=gpio-fan" $CONFIG ; then
      if ! tail -1 $CONFIG | grep -q "\\[all\\]" ; then
        sed $CONFIG -i -e "\$a[all]"
      fi
      sed $CONFIG -i -e "\$adtoverlay=gpio-fan,gpiopin=$GPIO,temp=$TEMP"
    else
      sed $CONFIG -i -e "s/^.*dtoverlay=gpio-fan.*/dtoverlay=gpio-fan,gpiopin=$GPIO,temp=$TEMP/"
    fi
    ASK_TO_REBOOT=1
    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "The fan on GPIO $GPIO is enabled and will turn on at $TIN degrees Celsius" 20 60 1
    fi
  else
    if grep -q "^dtoverlay=gpio-fan" $CONFIG ; then
      ASK_TO_REBOOT=1
    fi
    sed $CONFIG -i -e "/^.*dtoverlay=gpio-fan.*/d"
    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "The fan is disabled" 20 60 1
    fi
  fi
}

get_browser() {
  echo $(update-alternatives --display x-www-browser | grep currently | cut -d " " -f 7 | cut -d / -f 4)
}

do_browser() {
  if [ "$INTERACTIVE" = True ]; then
    RES=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Select Browser" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
      "1" "Chromium" \
      "2" "Firefox" \
      3>&1 1>&2 2>&3)
  else
    RES=""
    BROWSER=$1
    true
  fi
  if [ $? -eq 0 ]; then
    if [ "$RES" = "1" ] ; then
      BROWSER="chromium"
      BSTRING="Chromium"
    elif [ "$RES" = "2" ] ; then
      BROWSER="firefox"
      BSTRING="Firefox"
    fi
    update-alternatives --set x-www-browser /usr/bin/$BROWSER > /dev/null
    if [ -z $2 ] ; then
      sudo -u $USER xdg-settings set default-web-browser $BROWSER.desktop
    else
      sudo -u $2 xdg-settings set default-web-browser $BROWSER.desktop
    fi
    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "Default browser set to $BSTRING" 20 60 1
    fi
  fi
}

do_journald_storage() {
  if [ "$INTERACTIVE" = True ]; then
    RES=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Select Log Location" "$WT_HEIGHT" "$WT_WIDTH" "$WT_MENU_HEIGHT" \
      "1" "Default" \
      "2" "Volatile" \
      "3" "Persistent" \
      "4" "Auto" \
      "5" "None" \
      3>&1 1>&2 2>&3)
  else
    RES=""
    true
  fi
  # shellcheck disable=SC2181
  if [ $? -eq 0 ]; then
    case $RES in
      2) JSTRING="volatile"
      ;;
      3) JSTRING="persistent"
      ;;
      4) JSTRING="auto"
      ;;
      5) JSTRING="none"
      ;;
      *) JSTRING=""
      ;;
    esac

    # Restore any previous configuration in base configuration file to
    # commented-out systemd default as we now use drop-in configuration
    sed -i -E 's/^(Storage)=.*/#\1=auto/' /etc/systemd/journald.conf 2>/dev/null || true

    CONFIG_FILE="/etc/systemd/journald.conf.d/80-parch-config-journal-storage.conf"

    if [ -n "$JSTRING" ]; then
      # Create directory if it doesn't exist
      mkdir -p /etc/systemd/journald.conf.d
      # Create config file with the setting
      cat > "$CONFIG_FILE" << EOF
# Created/managed by parch-config

[Journal]
Storage=$JSTRING
EOF
    else
      # Remove our config file if it exists
      if [ -e "$CONFIG_FILE" ]; then
        rm -f "$CONFIG_FILE"
        # Try to remove directory if empty
        rmdir --ignore-fail-on-non-empty /etc/systemd/journald.conf.d 2>/dev/null
      fi
    fi

    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "Logging location set to ${JSTRING:-default}" 20 60 1
    fi
  fi
}

do_boot_target(){
  if [ "$INTERACTIVE" = True ]; then
    BOOTOPT=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Boot Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
      "B1 Console" "Text console" \
      "B2 Desktop" "Desktop GUI" \
      3>&1 1>&2 2>&3)
  else
    BOOTOPT=$1
    true
  fi
  if [ $? -eq 0 ]; then
    case "$BOOTOPT" in # Handle default target
      B1*) # Console
        systemctl --quiet set-default multi-user.target
        ;;
      B2*) # Desktop
        if [ -e /etc/init.d/lightdm ]; then
          systemctl --quiet set-default graphical.target
        else
          whiptail --msgbox "Do 'sudo pacman -S --noconfirm lightdm' to allow configuration of boot to desktop" 20 60 2
          return 1
        fi
        ;;
      *)
        whiptail --msgbox "Programmer error, unrecognised boot option" 20 60 2
        return 1
        ;;
    esac
  fi
  ASK_TO_REBOOT=1
}

do_autologin(){
  if [ -z "$USER" ]; then
    echo "Can't configure autologin: USER unset" >&2
    return 0
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like to automatically log in to the console?" $DEFAULT 20 60 2
    RET=$?
  else
    if [ $1 -eq 1 ] || [ $1 -eq 3 ] ; then
      RET=0
    else
      RET=1
    fi
  fi
  if [ $RET -eq 0 ] ; then
        mkdir -p /etc/systemd/system/getty@tty1.service.d
        cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin $USER --noclear %I \$TERM
EOF
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
        if [ -f /etc/systemd/system/getty@tty1.service.d/autologin.conf ]; then
          rm -f /etc/systemd/system/getty@tty1.service.d/autologin.conf
          rmdir --ignore-fail-on-non-empty /etc/systemd/system/getty@tty1.service.d
        fi
    STATUS=disabled
  else
    return $RET
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Console autologin is $STATUS" 20 60 1
    if [ $(get_boot_cli) -eq 0 ]; then
      return 0
    fi
    whiptail --yesno "Would you like to automatically log in to the desktop?" $DEFAULT 20 60 2
    RET=$?
  else
    if [ $1 -eq 2 ] || [ $1 -eq 3 ] ; then
      RET=0
    else
      RET=1
    fi
  fi
  if [ $RET -eq 0 ] ; then
    sed /etc/lightdm/lightdm.conf -i -e "s/^\(#\|\)autologin-user=.*/autologin-user=$USER/"
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    sed /etc/lightdm/lightdm.conf -i -e "s/^autologin-user=.*/#autologin-user=/"
    STATUS=disabled
  else
    return $RET
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Desktop autologin is $STATUS" 20 60 1
  fi
  ASK_TO_REBOOT=1
}

do_boot_behaviour() {
  if [ "$INTERACTIVE" = True ]; then
    BOOTOPT=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Boot Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
      "B1 Console" "Text console, requiring user to login" \
      "B2 Console Autologin" "Text console, automatically logged in as '$USER' user" \
      "B3 Desktop" "Desktop GUI, requiring user to login" \
      "B4 Desktop Autologin" "Desktop GUI, automatically logged in as '$USER' user" \
      3>&1 1>&2 2>&3)
  else
    BOOTOPT=$1
    true
  fi
  if [ $? -eq 0 ]; then
    case "$BOOTOPT" in # Handle default target
      B1*|B2*) # Console
        systemctl --quiet set-default multi-user.target
        ;;
      B3*|B4*) # Desktop
        if [ -e /etc/init.d/lightdm ]; then
          systemctl --quiet set-default graphical.target
        else
          whiptail --msgbox "Do 'sudo pacman -S --noconfirm lightdm' to allow configuration of boot to desktop" 20 60 2
          return 1
        fi
        ;;
      *)
        whiptail --msgbox "Programmer error, unrecognised boot option" 20 60 2
        return 1
        ;;
    esac
    case "$BOOTOPT" in # Handle autologin
      B1*|B3*) # Autologin disabled
        if [ -z "${BOOTOPT%%B3*}" ]; then
          sed /etc/lightdm/lightdm.conf -i -e "s/^autologin-user=.*/#autologin-user=/"
        fi
        if [ -f /etc/systemd/system/getty@tty1.service.d/autologin.conf ]; then
          rm -f /etc/systemd/system/getty@tty1.service.d/autologin.conf
          rmdir --ignore-fail-on-non-empty /etc/systemd/system/getty@tty1.service.d
        fi
        ;;
      B2*|B4*) # Autologin enabled
        if [ -z "$USER" ]; then
          echo "Can't configure autologin: USER unset" >&2
          return 0
        fi
        if [ -z "${BOOTOPT%%B4*}" ]; then
          sed /etc/lightdm/lightdm.conf -i -e "s/^\(#\|\)autologin-user=.*/autologin-user=$USER/"
        fi
        mkdir -p /etc/systemd/system/getty@tty1.service.d
        cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin $USER --noclear %I \$TERM
EOF
        ;;
    esac
    if [ "$INIT" = "systemd" ]; then
      systemctl daemon-reload
    fi
    ASK_TO_REBOOT=1
  fi
}

get_bootloader_filename() {
   CURDATE=$(date -d "$(vcgencmd bootloader_version |  head -n 1)" +%Y%m%d)
   FILNAME=""
   EEBASE=$(rpi-eeprom-update | grep RELEASE | sed 's/.*(//g' | sed 's/[^\/]*)//g')
   if grep FIRMWARE_RELEASE_STATUS /etc/default/rpi-eeprom-update | egrep -Eq "stable|latest"; then
      EEPATH="${EEBASE}/latest/pieeprom*.bin"
   else
      EEPATH="${EEBASE}/default/pieeprom*.bin"
   fi
   EXACT_MATCH=0
   for filename in $(find $EEPATH -name "pieeprom*.bin" 2>/dev/null | sort); do
      FILDATE=$(date -d "$(echo $filename | sed 's/.*\///g' | cut -d - -f 2- | cut -d . -f 1)" +%Y%m%d)
      FILNAME=$filename
      if [ $FILDATE -eq $CURDATE ]; then
         EXACT_MATCH=1
         break
      fi
   done
   if [ $EXACT_MATCH != 1 ]; then
      if [ "$INTERACTIVE" = True ]; then
         whiptail --yesno "Current EEPROM version $(date -d $CURDATE +%Y-%m-%d) or newer not found.\n\nTry updating the rpi-eeprom APT package.\n\nInstall latest local $(basename $FILNAME) anyway?" 20 70 3
         DEFAULTS=$?
         if [ "$DEFAULTS" -ne 0 ]; then
            FILNAME="none" # no
         fi
      fi
   fi
}

do_network_install_ui() {
  if [ "$INTERACTIVE" = True ]; then
    BOOTOPT=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Bootloader network install UI" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
      "B1 Always" "Always display the UI for a few seconds after power on." \
      "B2 On demand" "Display the UI if the SHIFT key is pressed or if an error occurs." \
      3>&1 1>&2 2>&3)
  else
    BOOTOPT=$1
    true
  fi

  if [ $? -eq 0 ]; then
    get_bootloader_filename
    if [ "${FILNAME}" = "none" ]; then
       if [ "$INTERACTIVE" = True ]; then
          return 0
       else
          return 1
       fi
    fi
    EECFG=$(mktemp)
    rpi-eeprom-config > $EECFG
    sed $EECFG -i -e "/NET_INSTALL_AT_POWER_ON/d"
    case "$BOOTOPT" in
      B1*)
         echo "NET_INSTALL_AT_POWER_ON=1" >> $EECFG
         ;;
      B2*)
         # NET_INSTALL_AT_POWER_ON default value is 0
         true
         ;;
      *)
        whiptail --msgbox "Programmer error, unrecognised boot option" 20 60 2
        return 1
        ;;
    esac
    rpi-eeprom-config --apply $EECFG $FILNAME
    ASK_TO_REBOOT=1
  fi
 }

do_libliftoff() {
  if [ "$INTERACTIVE" = True ]; then
    RET=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "libliftoff Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
        "L1 Disable" "Disable libliftoff hardware overlays" \
        "L2 Enable" "Enable libliftoff hardware overlays" \
        3>&1 1>&2 2>&3)
  else
    RET=$1
    true
  fi
  if [ $? -eq 0 ]; then
    case "$RET" in
      L1*)
        LIBLIFTOFF_ENABLED=0
        STATUS="disabled"
        ;;
      L2*)
        LIBLIFTOFF_ENABLED=1
        STATUS="enabled"
        ;;
      *)
        whiptail --msgbox "Programmer error, unrecognised libliftoff option" 20 60 2
        return 1
        ;;
    esac

    # Create the config dir and/or environment file, if needed
    if [ -z $HOMEDIR ]; then
      whiptail --msgbox "Unknown user home directory" 20 60 2
      return 1
    fi
    LABWC_DIR="$HOMEDIR/.config/labwc"
    if [ ! -e $LABWC_DIR ]; then
      mkdir -p $LABWC_DIR
      chown $USER:$USER $HOMEDIR/.config $LABWC_DIR
    fi
    if [ ! -e $LABWCENV_FILE ]; then
      touch $LABWCENV_FILE
      chown $USER:$USER $LABWCENV_FILE
    fi

    # If there is already a libliftoff line in the env file, replace it.
    # Otherwise, add one.
    if grep -q WLR_DRM_FORCE_LIBLIFTOFF $LABWCENV_FILE; then
      sed -i "s/^.*WLR_DRM_FORCE_LIBLIFTOFF.*$/WLR_DRM_FORCE_LIBLIFTOFF=$LIBLIFTOFF_ENABLED/" $LABWCENV_FILE
    else
      echo "WLR_DRM_FORCE_LIBLIFTOFF=$LIBLIFTOFF_ENABLED" >>$LABWCENV_FILE
    fi

    ASK_TO_REBOOT=1
  else
    return 0
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "libliftoff is $STATUS" 20 60 1
  fi
}

# shellcheck disable=SC2120
do_power_off_on_halt() {
  if [ "$INTERACTIVE" = True ]; then
    BOOTOPT=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Configure shutdown behaviour." "$WT_HEIGHT" "$WT_WIDTH" "$WT_MENU_HEIGHT" \
      "B1 Full power off" "Switch off Pi power management (PMIC) outputs on Pi4 / Pi5 (recommended)." \
      "B2 VPU sleep mode" "Compatibility mode for HATs (e.g. POE HAT) and GPIO power buttons (Pi4)." \
      3>&1 1>&2 2>&3)
  else
    BOOTOPT=$1
    true
  fi

  # shellcheck disable=SC2181
  if [ $? -eq 0 ]; then
    get_bootloader_filename
    if [ "${FILNAME}" = "none" ]; then
       if [ "$INTERACTIVE" = True ]; then
          return 0
       else
          return 1
       fi
    fi
    EECFG=$(mktemp)
    rpi-eeprom-config > "$EECFG"
    sed "$EECFG" -i -e "/POWER_OFF_ON_HALT/d"
    sed "$EECFG" -i -e "/WAKE_ON_GPIO/d"
    case "$BOOTOPT" in
      B1*)
         echo "POWER_OFF_ON_HALT=1" >> "$EECFG"
         echo "WAKE_ON_GPIO=0" >> "$EECFG"
         ;;
      B2*)
         echo "POWER_OFF_ON_HALT=0" >> "$EECFG"
         echo "WAKE_ON_GPIO=1" >> "$EECFG"
         true
         ;;
      *)
        whiptail --msgbox "Programmer error, unrecognised boot option" 20 60 2
        return 1
        ;;
    esac
    rpi-eeprom-config --apply "$EECFG" "$FILNAME"
    ASK_TO_REBOOT=1
  fi
 }

do_boot_order() {
  if [ "$INTERACTIVE" = True ]; then
    BOOTOPT=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Boot Device Order" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
      "B1 SD Card Boot " "Boot from SD Card before trying NVMe and then USB (RECOMMENDED)" \
      "B2 NVMe/USB Boot" "Boot from NVMe before trying USB and then SD Card" \
      "B3 Network Boot " "Boot from Network unless overridden by SD Card" \
      3>&1 1>&2 2>&3)
  else
    BOOTOPT=$1
    true
  fi
  if [ $? -eq 0 ]; then
    EECFG=$(mktemp)
    rpi-eeprom-config > $EECFG
    sed $EECFG -i -e "/SD_BOOT_MAX_RETRIES/d"
    sed $EECFG -i -e "/NET_BOOT_MAX_RETRIES/d"
    case "$BOOTOPT" in
      B1*)
        if is_pifive; then
           ORD=0xf461
        else
           ORD=0xf41
        fi
        if ! grep -q "BOOT_ORDER" $EECFG ; then
          sed $EECFG -i -e "\$a[all]\nBOOT_ORDER=${ORD}"
        else
          sed $EECFG -i -e "s/^BOOT_ORDER=.*/BOOT_ORDER=${ORD}/"
        fi
        STATUS="SD Card"
        ;;
      B2*)
        if is_pifive; then
           ORD=0xf146
        else
           ORD=0xf14
        fi
        if ! grep -q "BOOT_ORDER" $EECFG ; then
          sed $EECFG -i -e "\$a[all]\nBOOT_ORDER=${ORD}"
        else
          sed $EECFG -i -e "s/^BOOT_ORDER=.*/BOOT_ORDER=${ORD}/"
        fi
        STATUS="NVMe/USB"
        ;;
      B3*)
        if ! grep -q "BOOT_ORDER" $EECFG ; then
          sed $EECFG -i -e "\$a[all]\nBOOT_ORDER=0xf21"
        else
          sed $EECFG -i -e "s/^BOOT_ORDER=.*/BOOT_ORDER=0xf21/"
        fi
        STATUS="Network"
        ;;
      *)
        whiptail --msgbox "Programmer error, unrecognised boot option" 20 60 2
        return 1
        ;;
    esac
    get_bootloader_filename
    if [ "${FILNAME}" = "none" ]; then
       if [ "$INTERACTIVE" = True ]; then
          return 0
       else
          return 1
       fi
    fi
    rpi-eeprom-config --apply $EECFG $FILNAME
    ASK_TO_REBOOT=1
    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "$STATUS is default boot device" 20 60 1
    fi
  fi
}

do_beta_mode() {
  DVER=$(lsb_release -c | cut -f 2)

  if [ "$INTERACTIVE" = True ]; then
    BETAOPT=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Beta Access" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
      "B1 Beta Software" "Use the beta repository and latest bootloader image" \
      "B2 Release Software" "Use the release software repository" \
      3>&1 1>&2 2>&3)
  else
    BETAOPT=$1
    true
  fi
  if [ $? -eq 0 ]; then
    case "$BETAOPT" in
      B1*)
        if is_pifour || is_pifive ; then
          sed /etc/default/rpi-eeprom-update -i -e "s/^FIRMWARE_RELEASE_STATUS.*/FIRMWARE_RELEASE_STATUS=\"latest\"/"
        fi
        cat > /etc/apt/sources.list.d/beta.list << EOF
deb http://archive.raspberrypi.com/debian/ $DVER beta
#deb-src http://archive.raspberrypi.com/debian/ $DVER beta
EOF
        BETA="Beta"
        ;;
      B2*)
        rm -f /etc/apt/sources.list.d/beta.list
        BETA="Release"
        ;;
      *)
        whiptail --msgbox "Programmer error, unrecognised option" 20 60 2
        return 1
        ;;
    esac
    if [ "$INTERACTIVE" = True ]; then
         whiptail --msgbox "$BETA software enabled. Please use sudo apt update to refresh the repository." 20 60 2
    fi
     ASK_TO_REBOOT=1
  fi
}


do_boot_rom() {
  if [ "$INTERACTIVE" = True ]; then
    BOOTOPT=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Bootloader Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
      "E1 Latest" "Use the latest bootloader image" \
      "E2 Default" "Use the factory default bootloader image" \
      3>&1 1>&2 2>&3)
  else
    BOOTOPT=$1
    true
  fi
  if [ $? -eq 0 ]; then
    case "$BOOTOPT" in
      E1*)
        sed /etc/default/rpi-eeprom-update -i -e "s/^FIRMWARE_RELEASE_STATUS.*/FIRMWARE_RELEASE_STATUS=\"latest\"/"
        EETYPE="Latest version"
        ;;
      E2*)
        sed /etc/default/rpi-eeprom-update -i -e "s/^FIRMWARE_RELEASE_STATUS.*/FIRMWARE_RELEASE_STATUS=\"default\"/"
        EETYPE="Factory default"
        ;;
      *)
        whiptail --msgbox "Programmer error, unrecognised bootloader option" 20 60 2
        return 1
        ;;
    esac
    if [ "$INTERACTIVE" = True ]; then
      whiptail --yesno "$EETYPE bootloader selected - will be loaded at next reboot.\n\nReset bootloader to default configuration?" 20 60 2
      DEFAULTS=$?
    else
      DEFAULTS=$2
    fi
    if [ "$DEFAULTS" -eq 0 ]; then # yes
      get_bootloader_filename
      if [ "${FILNAME}" = "none" ]; then
         if [ "$INTERACTIVE" = True ]; then
            return 0
         else
            return 1
         fi
      fi
      rpi-eeprom-update -d -f $FILNAME
      if [ "$INTERACTIVE" = True ]; then
         whiptail --msgbox "Bootloader reset to default configuration" 20 60 2
      fi
    else
      if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "Bootloader not reset to defaults" 20 60 2
      fi
    fi
    ASK_TO_REBOOT=1
  fi
}

get_boot_splash() {
  if is_pi ; then
    if grep -q "splash" $CMDLINE ; then
      echo 0
    else
      echo 1
    fi
  else
    if grep -q "GRUB_CMDLINE_LINUX_DEFAULT.*splash" /etc/default/grub ; then
      echo 0
    else
      echo 1
    fi
  fi
}

do_boot_splash() {
  if [ ! -e /usr/share/plymouth/themes/pix/pix.script ]; then
    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "The splash screen is not installed so cannot be activated" 20 60 2
    fi
    return 1
  fi
  DEFAULT=--defaultno
  if [ $(get_boot_splash) -eq 0 ]; then
    DEFAULT=
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like to show the splash screen at boot?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq 0 ]; then
    if is_pi ; then
      if ! grep -q "splash" $CMDLINE ; then
        sed -i $CMDLINE -e "s/$/ quiet splash plymouth.ignore-serial-consoles/"
      fi
    else
      sed -i /etc/default/grub -e "s/GRUB_CMDLINE_LINUX_DEFAULT=\"\(.*\)\"/GRUB_CMDLINE_LINUX_DEFAULT=\"\1 quiet splash plymouth.ignore-serial-consoles\"/"
      sed -i /etc/default/grub -e "s/  \+/ /g"
      sed -i /etc/default/grub -e "s/GRUB_CMDLINE_LINUX_DEFAULT=\" /GRUB_CMDLINE_LINUX_DEFAULT=\"/"
      update-grub
    fi
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    if is_pi ; then
      if grep -q "splash" $CMDLINE ; then
        sed -i $CMDLINE -e "s/ quiet//"
        sed -i $CMDLINE -e "s/ splash//"
        sed -i $CMDLINE -e "s/ plymouth.ignore-serial-consoles//"
      fi
    else
      sed -i /etc/default/grub -e "s/GRUB_CMDLINE_LINUX_DEFAULT=\"\(.*\)quiet\(.*\)\"/GRUB_CMDLINE_LINUX_DEFAULT=\"\1\2\"/"
      sed -i /etc/default/grub -e "s/GRUB_CMDLINE_LINUX_DEFAULT=\"\(.*\)splash\(.*\)\"/GRUB_CMDLINE_LINUX_DEFAULT=\"\1\2\"/"
      sed -i /etc/default/grub -e "s/GRUB_CMDLINE_LINUX_DEFAULT=\"\(.*\)plymouth.ignore-serial-consoles\(.*\)\"/GRUB_CMDLINE_LINUX_DEFAULT=\"\1\2\"/"
      sed -i /etc/default/grub -e "s/  \+/ /g"
      sed -i /etc/default/grub -e "s/GRUB_CMDLINE_LINUX_DEFAULT=\" /GRUB_CMDLINE_LINUX_DEFAULT=\"/"
      update-grub
    fi
    STATUS=disabled
  else
    return $RET
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Splash screen at boot is $STATUS" 20 60 1
  fi
}

get_camera() {
  if [ $(deb_ver) -le 10 ]; then
    CAM=$(get_config_var start_x $CONFIG)
    if [ $CAM -eq 1 ]; then
      echo 0
    else
      echo 1
    fi
  else
    if grep -q camera_auto_detect $CONFIG ; then
      CAM=$(get_config_var camera_auto_detect $CONFIG)
      if [ $CAM -eq 1 ]; then
        echo 0
      else
        echo 1
      fi
    else
      echo 0
    fi
  fi
}

do_camera() {
  if [ $(deb_ver) -le 10 ] && [ ! -e /boot${FIRMWARE}/start_x.elf ]; then
    whiptail --msgbox "Your firmware appears to be out of date (no start_x.elf). Please update" 20 60 2
    return 1
  fi
  sed $CONFIG -i -e "s/^startx/#startx/"
  sed $CONFIG -i -e "s/^fixup_file/#fixup_file/"

  DEFAULT=--defaultno
  CURRENT=0
  if [ $(get_camera) -eq 0 ]; then
    DEFAULT=
    CURRENT=1
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like the camera interface to be enabled?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq $CURRENT ]; then
    ASK_TO_REBOOT=1
  fi
  if [ $RET -eq 0 ]; then
    if [ $(deb_ver) -le 10 ] ; then
      set_config_var start_x 1 $CONFIG
      CUR_GPU_MEM=$(get_config_var gpu_mem $CONFIG)
      if [ -z "$CUR_GPU_MEM" ] || [ "$CUR_GPU_MEM" -lt 128 ]; then
        set_config_var gpu_mem 128 $CONFIG
      fi
    else
      set_config_var camera_auto_detect 1 $CONFIG
    fi
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    if [ $(deb_ver) -le 10 ] ; then
      set_config_var start_x 0 $CONFIG
      sed $CONFIG -i -e "s/^start_file/#start_file/"
    else
      set_config_var camera_auto_detect 0 $CONFIG
    fi
    STATUS=disabled
  else
    return $RET
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The camera interface is $STATUS" 20 60 1
  fi
}

get_onewire() {
  if grep -q -E "^dtoverlay=w1-gpio" $CONFIG; then
    echo 0
  else
    echo 1
  fi
}

do_onewire() {
  DEFAULT=--defaultno
  CURRENT=0
  if [ $(get_onewire) -eq 0 ]; then
    DEFAULT=
    CURRENT=1
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like the one-wire interface to be enabled?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq $CURRENT ]; then
    ASK_TO_REBOOT=1
  fi
  if [ $RET -eq 0 ]; then
    sed $CONFIG -i -e "s/^#dtoverlay=w1-gpio/dtoverlay=w1-gpio/"
    if ! grep -q -E "^dtoverlay=w1-gpio" $CONFIG; then
      printf "dtoverlay=w1-gpio\n" >> $CONFIG
    fi
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    sed $CONFIG -i -e "s/^dtoverlay=w1-gpio/#dtoverlay=w1-gpio/"
    STATUS=disabled
  else
    return $RET
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The one-wire interface is $STATUS" 20 60 1
  fi
}

get_legacy() {
  if sed -n '/\[pi4\]/,/\[/ !p' $CONFIG | grep -q '^dtoverlay=vc4-kms-v3d' ; then
    echo 1
  else
    echo 0
  fi
}

do_legacy() {
  DEFAULT=--defaultno
  CURRENT=0
  if [ $(get_legacy) -eq 0 ]; then
    DEFAULT=
    CURRENT=1
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like to enable legacy camera support?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq $CURRENT ]; then
    ASK_TO_REBOOT=1
  fi
  if [ $RET -eq 0 ]; then
    sed $CONFIG -i -e '/\[pi4\]/,/\[/ s/^#\?dtoverlay=vc4-f\?kms-v3d/dtoverlay=vc4-fkms-v3d/g'
    sed $CONFIG -i -e '/\[pi4\]/,/\[/ !s/^dtoverlay=vc4-kms-v3d/#dtoverlay=vc4-kms-v3d/g'
    sed $CONFIG -i -e '/\[pi4\]/,/\[/ !s/^dtoverlay=vc4-fkms-v3d/#dtoverlay=vc4-fkms-v3d/g'
    if ! sed -n '/\[pi4\]/,/\[/ p' $CONFIG | grep -q '^dtoverlay=vc4-fkms-v3d' ; then
      if grep -q '[pi4]' $CONFIG ; then
        sed $CONFIG -i -e 's/\[pi4\]/\[pi4\]\ndtoverlay=vc4-fkms-v3d/'
      else
        printf "[pi4]\ndtoverlay=vc4-fkms-v3d\n" >> $CONFIG
      fi
    fi
    CUR_GPU_MEM=$(get_config_var gpu_mem $CONFIG)
    if [ -z "$CUR_GPU_MEM" ] || [ "$CUR_GPU_MEM" -lt 128 ]; then
      set_config_var gpu_mem 128 $CONFIG
    fi
    sed $CONFIG -i -e 's/^camera_auto_detect.*/start_x=1/g'
    sed $CONFIG -i -e 's/^dtoverlay=camera/#dtoverlay=camera/g'
    STATUS="Legacy camera support is enabled.\n\nPlease note that this functionality is deprecated and will not be supported for future development."
  else
    sed $CONFIG -i -e 's/^#\?dtoverlay=vc4-f\?kms-v3d/dtoverlay=vc4-kms-v3d/g'
    sed $CONFIG -i -e '/\[pi4\]/,/\[/ {/dtoverlay=vc4-kms-v3d/d}'
    if ! sed -n '/\[pi4\]/,/\[/ !p' $CONFIG | grep -q '^dtoverlay=vc4-kms-v3d' ; then
      if grep -q '[all]' $CONFIG ; then
        sed $CONFIG -i -e 's/\[all\]/\[all\]\ndtoverlay=vc4-kms-v3d/'
      else
        printf "[all]\ndtoverlay=vc4-kms-v3d\n" >> $CONFIG
      fi
    fi
    sed $CONFIG -i -e 's/^start_x.*/camera_auto_detect=1/g'
    sed $CONFIG -i -e 's/^#dtoverlay=camera/dtoverlay=camera/g'
    STATUS="Legacy camera support is disabled."
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "$STATUS" 20 60 1
  fi
}

do_gldriver() {
  if [ ! -e /boot${FIRMWARE}/overlays/vc4-kms-v3d.dtbo ]; then
    whiptail --msgbox "Driver and kernel not present on your system. Please update" 20 60 2
    return 1
  fi
  for package in gldriver-test libgl1-mesa-dri; do
    if ! is_installed "$package"; then
      missing_packages="$package $missing_packages"
    fi
  done
  if [ -n "$missing_packages" ] && ! pacman -S --noconfirm $missing_packages; then
    whiptail --msgbox "Required packages not found, please install: ${missing_packages}" 20 60 2
    return 1
  fi
  GLOPT=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "GL Driver" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
    "G1 Legacy" "Original non-GL desktop driver" \
    "G2 GL (Full KMS)" "OpenGL desktop driver with full KMS" \
    3>&1 1>&2 2>&3)
  if [ $? -eq 0 ]; then
    case "$GLOPT" in
      G1*)
        if sed -n "/\[pi4\]/,/\[/ !p" $CONFIG | grep -q -E "^dtoverlay=vc4-f?kms-v3d" ; then
          ASK_TO_REBOOT=1
        fi
        sed $CONFIG -i -e "s/^dtoverlay=vc4-fkms-v3d/#dtoverlay=vc4-fkms-v3d/g"
        sed $CONFIG -i -e "s/^dtoverlay=vc4-kms-v3d/#dtoverlay=vc4-kms-v3d/g"
        STATUS="The GL driver is disabled."
        ;;
      G2*)
        if ! sed -n "/\[pi4\]/,/\[/ !p" $CONFIG | grep -q "^dtoverlay=vc4-kms-v3d" ; then
          ASK_TO_REBOOT=1
        fi
        sed $CONFIG -i -e "s/^dtoverlay=vc4-fkms-v3d/#dtoverlay=vc4-fkms-v3d/g"
        sed $CONFIG -i -e "s/^#dtoverlay=vc4-kms-v3d/dtoverlay=vc4-kms-v3d/g"
        if ! sed -n "/\[pi4\]/,/\[/ !p" $CONFIG | grep -q "^dtoverlay=vc4-kms-v3d" ; then
          printf "[all]\ndtoverlay=vc4-kms-v3d\n" >> $CONFIG
        fi
        STATUS="The full KMS GL driver is enabled."
        ;;
      *)
        whiptail --msgbox "Programmer error, unrecognised boot option" 20 60 2
        return 1
        ;;
    esac
  else
    return 0
  fi
  whiptail --msgbox "$STATUS" 20 60 1
}

do_xcompmgr() {
  DEFAULT=--defaultno
  CURRENT=0
  if [ -e /etc/xdg/autostart/xcompmgr.desktop ]; then
    DEFAULT=
    CURRENT=1
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like the xcompmgr composition manager to be enabled?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq $CURRENT ]; then
    ASK_TO_REBOOT=1
  fi
  if [ $RET -eq 0 ]; then
    if [ ! -e /usr/bin/xcompmgr ] ; then
      if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "To install xcompmgr:\n\nsudo pacman -S xcompmgr" 20 60 1
      fi
    fi
    cat << EOF > /etc/xdg/autostart/xcompmgr.desktop
[Desktop Entry]
Type=Application
Name=xcompmgr
Comment=Start xcompmgr compositor
NoDisplay=true
Exec=/usr/lib/parch-config/cmstart.sh
EOF
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    rm -f /etc/xdg/autostart/xcompmgr.desktop
    STATUS=disabled
  else
    return $RET
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The xcompmgr composition manager is $STATUS" 20 60 1
  fi
}

do_glamor() {
  DEFAULT=
  CURRENT=1
  if [ -e /usr/share/X11/xorg.conf.d/20-noglamor.conf ] ; then
    DEFAULT=--defaultno
    CURRENT=0
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like glamor acceleration to be enabled?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq $CURRENT ]; then
    ASK_TO_REBOOT=1
  fi
  if [ $RET -eq 0 ]; then
    systemctl disable glamor-test.service
    systemctl stop glamor-test.service
    rm -f /usr/share/X11/xorg.conf.d/20-noglamor.conf
    STATUS=enabled
  elif [ $RET -eq 1 ]; then
    systemctl enable glamor-test.service &&
    systemctl start glamor-test.service &&
    STATUS=disabled
  else
    return $RET
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Glamor acceleration is $STATUS" 20 60 1
  fi
}

do_wayland() {
  if [ "$INTERACTIVE" = True ]; then
    RET=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Wayland Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
        "W1 X11" "Openbox window manager with X11 backend" \
        "W2 Labwc" "Labwc window manager with Wayland backend" \
        3>&1 1>&2 2>&3)
  else
    RET=$1
    true
  fi
  if [ $? -eq 0 ]; then
    if [ -f /usr/share/xsessions/rpd-x.desktop ] || [ -f /usr/share/wayland-sessions/rpd-labwc.desktop ]; then
      XSESSION=rpd-x
      WSESSION=rpd-labwc
      XGSESSION=pi-greeter-x
      WGSESSION=pi-greeter-labwc
    else
      XSESSION=LXDE-pi-x
      WSESSION=LXDE-pi-labwc
      XGSESSION=pi-greeter
      WGSESSION=pi-greeter-labwc
    fi
    case "$RET" in
      W1*)
        sed /etc/lightdm/lightdm.conf -i -e "s/^#\\?user-session.*/user-session=$XSESSION/"
        sed /etc/lightdm/lightdm.conf -i -e "s/^#\\?autologin-session.*/autologin-session=$XSESSION/"
        sed /etc/lightdm/lightdm.conf -i -e "s/^#\\?greeter-session.*/greeter-session=$XGSESSION/"
        sed /etc/lightdm/lightdm.conf -i -e "s/^fallback-test.*/#fallback-test=/"
        sed /etc/lightdm/lightdm.conf -i -e "s/^fallback-session.*/#fallback-session=/"
        sed /etc/lightdm/lightdm.conf -i -e "s/^fallback-greeter.*/#fallback-greeter=/"
        if [ -e "/var/lib/AccountsService/users/$USER" ] ; then
          sed "/var/lib/AccountsService/users/$USER" -i -e "s/XSession=.*/XSession=$XSESSION/"
        fi
        STATUS="Openbox on X11"
        ;;
      W2*)
        sed /etc/lightdm/lightdm.conf -i -e "s/^#\\?user-session.*/user-session=$WSESSION/"
        sed /etc/lightdm/lightdm.conf -i -e "s/^#\\?autologin-session.*/autologin-session=$WSESSION/"
        sed /etc/lightdm/lightdm.conf -i -e "s/^#\\?greeter-session.*/greeter-session=$WGSESSION/"
        sed /etc/lightdm/lightdm.conf -i -e "s/^fallback-test.*/#fallback-test=/"
        sed /etc/lightdm/lightdm.conf -i -e "s/^fallback-session.*/#fallback-session=/"
        sed /etc/lightdm/lightdm.conf -i -e "s/^fallback-greeter.*/#fallback-greeter=/"
        if [ -e "/var/lib/AccountsService/users/$USER" ] ; then
          sed "/var/lib/AccountsService/users/$USER" -i -e "s/XSession=.*/XSession=$WSESSION/"
        fi
        STATUS="Labwc on Wayland"
        ;;
      *)
        whiptail --msgbox "Programmer error, unrecognised boot option" 20 60 2
        return 1
        ;;
    esac
    ASK_TO_REBOOT=1
  else
    return 0
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "$STATUS is active" 20 60 1
  fi
}

get_net_names() {
  if grep -q "net.ifnames=0" $CMDLINE || \
    ( [ "$(readlink -f /etc/systemd/network/99-default.link)" = "/dev/null" ] && \
      [ "$(readlink -f /etc/systemd/network/73-usb-net-by-mac.link)" = "/dev/null" ] ); then
    echo 1
  else
    echo 0
  fi
}

do_net_names () {
  DEFAULT=--defaultno
  CURRENT=0
  if [ "$(get_net_names)" -eq 0 ]; then
    DEFAULT=
    CURRENT=1
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like to enable predictable network interface names?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ "$RET" -eq $CURRENT ]; then
    ASK_TO_REBOOT=1
  fi
  if [ "$RET" -eq 0 ]; then
    sed -i "$CMDLINE" -e "s/net.ifnames=0 *//"
    rm -f /etc/systemd/network/99-default.link
    rm -f /etc/systemd/network/73-usb-net-by-mac.link
    STATUS=enabled
  elif [ "$RET" -eq 1 ]; then
    ln -sf /dev/null /etc/systemd/network/99-default.link
    ln -sf /dev/null /etc/systemd/network/73-usb-net-by-mac.link
    STATUS=disabled
  else
    return "$RET"
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Predictable network interface names are $STATUS" 20 60 1
  fi
}

do_update() {
  pacman -Sy &&
  pacman -S --noconfirm parch-config &&
  printf "Sleeping 5 seconds before reloading parch-config\n" &&
  sleep 5 &&
  exec parch-config
}

do_audio() {
  if is_pulseaudio ; then
    whiptail --msgbox "parch-config cannot configure audio when PulseAudio or PipeWire are in use.\n\nThese can be controlled with the taskbar plugin." 20 60 1
    return 0
  else
    if aplay -l | grep -q "bcm2835 ALSA"; then
      if [ "$INTERACTIVE" = True ]; then
        AUDIO_OUT=$(whiptail --menu "Choose the audio output" 20 60 10 \
          "0" "Auto" \
          "1" "Force 3.5mm ('headphone') jack" \
          "2" "Force HDMI" \
          3>&1 1>&2 2>&3)
      else
        AUDIO_OUT=$1
      fi
      if [ "$?" -eq 0 ] && [ -n "$AUDIO_OUT" ]; then
        amixer cset numid=3 "$AUDIO_OUT"
      fi
    else
      ASPATH=$(getent passwd $USER | cut -d : -f 6)/.asoundrc
      if [ "$INTERACTIVE" = True ]; then
        n=0
        array=""
        while [ $n -le 9 ]
        do
          CARD=$(LC_ALL=C aplay -l | grep "card $n" | cut -d [ -f 2 | cut -d ] -f 1)
          if [ -z "$CARD" ] ; then
            break
          else
            if [ -z "$array" ] ; then
              array=$n"/"$CARD
            else
              #shellcheck disable=2027
              array=$array"/"$n"/"$CARD
            fi
          fi
          n=$(( n+1 ))
        done
        if [ $n -eq 0 ] ; then
          whiptail --msgbox "No audio devices found" 20 60 1
          false
        else
          oIFS="$IFS"
          IFS="/"
          AUDIO_OUT=$(whiptail --menu "Choose the audio output" 20 60 10 ${array} 3>&1 1>&2 2>&3)
          IFS="$oIFS"
        fi
      else
        AUDIO_OUT=$1
      fi
      if [ "$?" -eq 0 ] && [ -n "$AUDIO_OUT" ]; then
        cat << EOF > $ASPATH
pcm.!default {
  type asym
  playback.pcm {
    type plug
    slave.pcm "output"
  }
  capture.pcm {
    type plug
    slave.pcm "input"
  }
}

pcm.output {
  type hw
  card $AUDIO_OUT
}

ctl.!default {
  type hw
  card $AUDIO_OUT
}
EOF
      fi
    fi
  fi
}

do_resolution() {
  if [ "$INTERACTIVE" = True ]; then
    CMODE=$(get_config_var hdmi_mode $CONFIG)
    CGROUP=$(get_config_var hdmi_group $CONFIG)
    if [ $CMODE -eq 0 ] ; then
      CSET="Default"
    elif [ $CGROUP -eq 2 ] ; then
      CSET="DMT Mode "$CMODE
    else
      CSET="CEA Mode "$CMODE
    fi
    oIFS="$IFS"
    IFS="/"
    if tvservice -d /dev/null | grep -q Nothing ; then
      value="Default/720x480/DMT Mode 4/640x480 60Hz 4:3/DMT Mode 9/800x600 60Hz 4:3/DMT Mode 16/1024x768 60Hz 4:3/DMT Mode 85/1280x720 60Hz 16:9/DMT Mode 35/1280x1024 60Hz 5:4/DMT Mode 51/1600x1200 60Hz 4:3/DMT Mode 82/1920x1080 60Hz 16:9/"
    else
      value="Default/Monitor preferred resolution/"
      value=$value$(tvservice -m CEA | grep progressive | cut -b 12- | sed 's/mode \([0-9]\+\): \([0-9]\+\)x\([0-9]\+\) @ \([0-9]\+\)Hz \([0-9]\+\):\([0-9]\+\), clock:[0-9]\+MHz progressive/CEA Mode \1\/\2x\3 \4Hz \5:\6/' | tr '\n' '/')
      value=$value$(tvservice -m DMT | grep progressive | cut -b 12- | sed 's/mode \([0-9]\+\): \([0-9]\+\)x\([0-9]\+\) @ \([0-9]\+\)Hz \([0-9]\+\):\([0-9]\+\), clock:[0-9]\+MHz progressive/DMT Mode \1\/\2x\3 \4Hz \5:\6/' | tr '\n' '/')
    fi
    RES=$(whiptail --default-item $CSET --menu "Choose screen resolution" 20 60 10 ${value} 3>&1 1>&2 2>&3)
    STATUS=$?
    IFS="$oIFS"
    if [ $STATUS -eq 0 ] ; then
      GRS=$(echo "$RES" | cut -d ' ' -f 1)
      MODE=$(echo "$RES" | cut -d ' ' -f 3)
      if [ $GRS = "Default" ] ; then
        MODE=0
      elif [ $GRS = "DMT" ] ; then
        GROUP=2
      else
        GROUP=1
      fi
    fi
  else
    GROUP=$1
    MODE=$2
    STATUS=0
  fi
  if [ $STATUS -eq 0 ]; then
    if [ $MODE -eq 0 ]; then
      clear_config_var hdmi_force_hotplug $CONFIG
      clear_config_var hdmi_group $CONFIG
      clear_config_var hdmi_mode $CONFIG
    else
      set_config_var hdmi_force_hotplug 1 $CONFIG
      set_config_var hdmi_group $GROUP $CONFIG
      set_config_var hdmi_mode $MODE $CONFIG
    fi
    if [ "$INTERACTIVE" = True ]; then
      if [ $MODE -eq 0 ] ; then
        whiptail --msgbox "The resolution is set to default" 20 60 1
      else
        whiptail --msgbox "The resolution is set to $GRS mode $MODE" 20 60 1
      fi
    fi
    if [ $MODE -eq 0 ] ; then
      TSET="Default"
    elif [ $GROUP -eq 2 ] ; then
      TSET="DMT Mode "$MODE
    else
      TSET="CEA Mode "$MODE
    fi
    if [ "$TSET" != "$CSET" ] ; then
      ASK_TO_REBOOT=1
    fi
  fi
}

get_vnc_resolution() {
  if is_wayfire ; then
    W=$(grep ^headless_width $WAYFIRE_FILE | grep -o "[0-9]*")
    H=$(grep ^headless_height $WAYFIRE_FILE | grep -o "[0-9]*")
    if [ -n "$W" ] && [ -n "$H" ] ; then
      echo $W"x"$H
    else
      echo "1280x720"
    fi
  else
    if [ -e /etc/xdg/autostart/vnc_xrandr.desktop ] ; then
      grep fb /etc/xdg/autostart/vnc_xrandr.desktop | cut -f 15 -d ' '
    else
      echo ""
    fi
  fi
}

do_vnc_resolution() {
  if [ "$INTERACTIVE" = True ]; then
    CUR=$(get_vnc_resolution)
    if [ "$CUR" = "" ] ; then
      CUR=640x480
    fi
    FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --default-item $CUR --menu "Set VNC Resolution" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
      "640x480" "" "720x480" "" "800x600" "" "1024x768" "" "1280x720" "" "1280x1024" "" "1600x1200" "" "1920x1080" "" 3>&1 1>&2 2>&3)
    RET=$?
  else
    FUN=$1
    RET=0
  fi
  if [ $RET -eq 1 ]; then
    return 0
  elif [ $RET -eq 0 ]; then
    if is_wayfire ; then
      W=$(echo "$FUN" | cut -d x -f 1)
      H=$(echo "$FUN" | cut -d x -f 2)
      if grep -q ^headless_width $WAYFIRE_FILE ; then
        sed -i "s/headless_width.*/headless_width = $W/" $WAYFIRE_FILE
        sed -i "s/headless_height.*/headless_height = $H/" $WAYFIRE_FILE
      else
        if grep -q "\[output\]" $WAYFIRE_FILE ; then
          sed -i "s/\[output]/[output]\nheadless_width = $W\nheadless_height = $H/" $WAYFIRE_FILE
        else
          printf '\n[output]\nheadless_width = %d\nheadless_height = %d\n' "$W" "$H" >> $WAYFIRE_FILE
        fi
      fi
    else
      cat > /etc/xdg/autostart/vnc_xrandr.desktop << EOF
[Desktop Entry]
Type=Application
Name=vnc_xrandr
Comment=Set resolution for VNC
NoDisplay=true
Exec=sh -c "if ! (xrandr | grep -q -w connected) ; then /usr/bin/xrandr --fb $FUN ; fi"
EOF
    fi
    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "The resolution is set to $FUN" 20 60 1
      ASK_TO_REBOOT=1
    fi
  fi
}

list_wlan_interfaces() {
  for dir in /sys/class/net/*/wireless; do
    if [ -d "$dir" ]; then
      IFACE="$(basename "$(dirname "$dir")")"
      if wpa_cli -i "$IFACE" status > /dev/null 2>&1; then
        echo "$IFACE"
      fi
    fi
  done
}

do_wifi_ssid_passphrase() {
  RET=0
  if [ "$INTERACTIVE" = True ] && [ -z "$(get_wifi_country)" ]; then
    do_wifi_country
  fi

  if systemctl -q is-active dhcpcd; then
    IFACE="$(list_wlan_interfaces | head -n 1)"

    if [ -z "$IFACE" ]; then
      if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "No wireless interface found" 20 60
      fi
      return 1
    fi

    if ! wpa_cli -i "$IFACE" status > /dev/null 2>&1; then
      if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "Could not communicate with wpa_supplicant" 20 60
      fi
      return 1
    fi
  elif ! systemctl -q is-active NetworkManager; then
    if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "No supported network connection manager found" 20 60
      fi
      return 1
  fi

  SSID="$1"
  while [ -z "$SSID" ] && [ "$INTERACTIVE" = True ]; do
    if ! SSID=$(whiptail --inputbox "Please enter SSID" 20 60 3>&1 1>&2 2>&3); then
      return 0
    elif [ -z "$SSID" ]; then
      whiptail --msgbox "SSID cannot be empty. Please try again." 20 60
    fi
  done

  PASSPHRASE="$2"
  while [ "$INTERACTIVE" = True ]; do
    if ! PASSPHRASE=$(whiptail --passwordbox "Please enter passphrase. Leave it empty if none." 20 60 3>&1 1>&2 2>&3); then
      return 0
    else
      break
    fi
  done

  # Escape special characters for embedding in regex below
  ssid="$(echo "$SSID" \
   | sed 's;\\;\\\\;g' \
   | sed -e 's;\.;\\\.;g' \
         -e 's;\*;\\\*;g' \
         -e 's;\+;\\\+;g' \
         -e 's;\?;\\\?;g' \
         -e 's;\^;\\\^;g' \
         -e 's;\$;\\\$;g' \
         -e 's;\/;\\\/;g' \
         -e 's;\[;\\\[;g' \
         -e 's;\];\\\];g' \
         -e 's;{;\\{;g'   \
         -e 's;};\\};g'   \
         -e 's;(;\\(;g'   \
         -e 's;);\\);g'   \
         -e 's;";\\\\\";g')"

  HIDDEN=${3:-0}
  PLAIN=${4:-1}

  if systemctl -q is-active dhcpcd; then
    wpa_cli -i "$IFACE" list_networks \
     | tail -n +2 | cut -f -2 | grep -P "\t$ssid$" | cut -f1 \
     | while read -r ID; do
      wpa_cli -i "$IFACE" remove_network "$ID" > /dev/null 2>&1
    done

    ID="$(wpa_cli -i "$IFACE" add_network)"
    wpa_cli -i "$IFACE" set_network "$ID" ssid "\"$SSID\"" 2>&1 | grep -q "OK"
    RET=$((RET + $?))

    if [ -z "$PASSPHRASE" ]; then
      wpa_cli -i "$IFACE" set_network "$ID" key_mgmt NONE 2>&1 | grep -q "OK"
      RET=$((RET + $?))
    else
      if [ "$PLAIN" = 1 ]; then
        PASSPHRASE="\"$PASSPHRASE\""
      fi
      wpa_cli -i "$IFACE" set_network "$ID" psk "$PASSPHRASE" 2>&1 | grep -q "OK"
      RET=$((RET + $?))
    fi
    if [ "$HIDDEN" -ne 0 ]; then
      wpa_cli -i "$IFACE" set_network "$ID" scan_ssid 1 2>&1 | grep -q "OK"
      RET=$((RET + $?))
    fi
    if [ $RET -eq 0 ]; then
      wpa_cli -i "$IFACE" enable_network "$ID" > /dev/null 2>&1
    else
      wpa_cli -i "$IFACE" remove_network "$ID" > /dev/null 2>&1
      if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "Failed to set SSID or passphrase" 20 60
      fi
    fi
    wpa_cli -i "$IFACE" save_config > /dev/null 2>&1
    echo "$IFACE_LIST" | while read -r IFACE; do
      wpa_cli -i "$IFACE" reconfigure > /dev/null 2>&1
    done
  else
    IFACE="$(list_wlan_interfaces | head -n 1)"
    if [ "$HIDDEN" -ne 0 ]; then
      nmcli device wifi connect "$SSID" password "$PASSPHRASE" ifname "${IFACE}" hidden true | grep -q "activated"
    else
      nmcli device wifi connect "$SSID" password "$PASSPHRASE" ifname "${IFACE}" | grep -q "activated"
    fi
    RET=$((RET + $?))
  fi

  return "$RET"
}

get_wifi_power_save() {
  # Check if the configuration file exists (simpler approach)
  if [ -f /etc/NetworkManager/conf.d/default-wifi-powersave-off.conf ]; then
    echo 0  # Power saving is disabled
  else
    echo 1  # Power saving is enabled (default)
  fi
}

# shellcheck disable=SC2120
do_wifi_power_save() {
  DEFAULT=
  CURRENT=0
  if [ "$(get_wifi_power_save)" -eq 0 ]; then
    DEFAULT=--defaultno
    CURRENT=1
  fi

  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like to enable WLAN power saving?\n\nNote: Disabling power saving may improve connection stability but increase power consumption." $DEFAULT 20 70 2
    RET=$?
  else
    RET=$1
  fi

  # Create NetworkManager conf.d directory if it doesn't exist
  mkdir -p /etc/NetworkManager/conf.d

  if [ "$RET" -eq 0 ]; then
    # Enable power saving - remove the configuration file
    rm -f /etc/NetworkManager/conf.d/default-wifi-powersave-off.conf
    STATUS="enabled"
  elif [ "$RET" -eq 1 ]; then
    # Disable power saving - create configuration file
    cat > /etc/NetworkManager/conf.d/default-wifi-powersave-off.conf << EOF
[connection]
wifi.powersave = 2
EOF
    STATUS="disabled"
  else
    return "$RET"
  fi

  # Reload NetworkManager configuration for persistence
  if systemctl -q is-active NetworkManager; then
    systemctl reload NetworkManager 2>/dev/null || true
  fi

  # Apply the change immediately to current interface
  IFACE="$(list_wlan_interfaces | head -n 1)"
  if [ -n "$IFACE" ] && command -v iw >/dev/null 2>&1; then
    if [ "$RET" -eq 0 ]; then
      iw dev "$IFACE" set power_save on 2>/dev/null || true
    else
      iw dev "$IFACE" set power_save off 2>/dev/null || true
    fi
  fi

  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "WLAN power saving is $STATUS\n\nThe change has been applied immediately and made persistent for future connections." 20 75 1
  fi
}

get_link_local_fallback() {
  # Check if the configuration file exists
  if [ -f /etc/NetworkManager/conf.d/link-local.conf ]; then
    echo 1  # Link-local fallback is enabled
  else
    echo 0  # Link-local fallback is disabled (default)
  fi
}

# shellcheck disable=SC2120
do_link_local_fallback() {
  DEFAULT=--defaultno
  CURRENT=$(get_link_local_fallback)
  if [ "$CURRENT" -eq 1 ]; then
    DEFAULT=
  fi

  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like to enable link-local fallback?\n\nNote: This assigns a 169.254.x.x address immediately, before trying DHCP. Enable only if you understand this behavior." $DEFAULT 20 75 2
    RET=$?
  else
    RET=$1
  fi

  if [ "$RET" -eq 0 ]; then
    STATUS="enabled"
  else
    STATUS="disabled"
  fi

  # Only make changes if the setting is actually changing
  if [ "$RET" -eq 0 ] && [ "$CURRENT" -eq 0 ]; then
    # Enable link-local fallback
    mkdir -p /etc/NetworkManager/conf.d
    cat > /etc/NetworkManager/conf.d/link-local.conf << EOF
# Created/managed by parch-config
[connection]
ipv4.link-local=4
EOF
    ASK_TO_REBOOT=1
  elif [ "$RET" -eq 1 ] && [ "$CURRENT" -eq 1 ]; then
    # Disable link-local fallback
    rm -f /etc/NetworkManager/conf.d/link-local.conf
    ASK_TO_REBOOT=1
  fi

  # Reload NetworkManager configuration for persistence
  if systemctl -q is-active NetworkManager; then
    systemctl reload NetworkManager 2>/dev/null || true
  fi

  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Link-local fallback is $STATUS" 20 60 1
  fi
}

do_finish() {
  disable_raspi_config_at_boot
  if [ $ASK_TO_REBOOT -eq 1 ]; then
    whiptail --yesno "Would you like to reboot now?" 20 60 2
    if [ $? -eq 0 ]; then # yes
      sync
      reboot
    fi
  fi
  exit 0
}

# $1 = filename, $2 = key name
get_json_string_val() {
  sed -n -e "s/^[[:space:]]*\"$2\"[[:space:]]*:[[:space:]]*\"\(.*\)\"[[:space:]]*,$/\1/p" $1
}

# TODO: This is probably broken
do_apply_os_config() {
  [ -e /boot/os_config.json ] || return 0
  NOOBSFLAVOUR=$(get_json_string_val /boot/os_config.json flavour)
  NOOBSLANGUAGE=$(get_json_string_val /boot/os_config.json language)
  NOOBSKEYBOARD=$(get_json_string_val /boot/os_config.json keyboard)

  if [ -n "$NOOBSFLAVOUR" ]; then
    printf "Setting flavour to %s based on os_config.json from NOOBS. May take a while\n" "$NOOBSFLAVOUR"

    printf "Unrecognised flavour. Ignoring\n"
  fi

  # TODO: currently ignores en_gb settings as we assume we are running in a
  # first boot context, where UK English settings are default
  case "$NOOBSLANGUAGE" in
    "en")
      if [ "$NOOBSKEYBOARD" = "gb" ]; then
        DEBLANGUAGE="" # UK english is the default, so ignore
      else
        DEBLANGUAGE="en_US.UTF-8"
      fi
      ;;
    "de")
      DEBLANGUAGE="de_DE.UTF-8"
      ;;
    "fi")
      DEBLANGUAGE="fi_FI.UTF-8"
      ;;
    "fr")
      DEBLANGUAGE="fr_FR.UTF-8"
      ;;
    "hu")
      DEBLANGUAGE="hu_HU.UTF-8"
      ;;
    "ja")
      DEBLANGUAGE="ja_JP.UTF-8"
      ;;
    "nl")
      DEBLANGUAGE="nl_NL.UTF-8"
      ;;
    "pt")
      DEBLANGUAGE="pt_PT.UTF-8"
      ;;
    "ru")
      DEBLANGUAGE="ru_RU.UTF-8"
      ;;
    "zh_CN")
      DEBLANGUAGE="zh_CN.UTF-8"
      ;;
    *)
      printf "Language '%s' not handled currently. Run sudo parch-config to set up" "$NOOBSLANGUAGE"
      ;;
  esac

  if [ -n "$DEBLANGUAGE" ]; then
    printf "Setting language to %s based on os_config.json from NOOBS. May take a while\n" "$DEBLANGUAGE"
    do_change_locale "$DEBLANGUAGE"
  fi

  if [ -n "$NOOBSKEYBOARD" -a "$NOOBSKEYBOARD" != "gb" ]; then
    printf "Setting keyboard layout to %s based on os_config.json from NOOBS. May take a while\n" "$NOOBSKEYBOARD"
    do_configure_keyboard "$NOOBSKEYBOARD"
  fi
  return 0
}

get_overlay_now() {
  grep -q "overlayroot=tmpfs" /proc/cmdline
  echo $?
}

get_overlay_conf() {
  grep -q "overlayroot=tmpfs" $CMDLINE
  echo $?
}

get_bootro_now() {
  findmnt /boot${FIRMWARE} | grep -q " ro,"
  echo $?
}

get_bootro_conf() {
  grep /boot${FIRMWARE} /etc/fstab | grep -q "defaults.*,ro[ ,]"
  echo $?
}

is_uname_current() {
  test -d "/lib/modules/$(uname -r)"
}

enable_overlayfs() {
  if [ "$(awk '/MemTotal/{print $2; exit}' /proc/meminfo)" -le 262144 ]; then
    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "At least 512MB of RAM is recommended for overlay filesystem" 20 60 1
    else
      echo "At least 512MB of RAM is recommended for overlay filesystem"
    fi
    return 1
  fi
  is_installed overlayroot || pacman -S --noconfirm -y overlayroot
  # mount the boot partition as writable if it isn't already
  if [ $(get_bootro_now) -eq 0 ] ; then
    if ! mount -o remount,rw /boot${FIRMWARE} 2>/dev/null ; then
      echo "Unable to mount boot partition as writable - cannot enable"
      return 1
    fi
    BOOTRO=yes
  else
    BOOTRO=no
  fi

  # modify command line
  if ! grep -q "overlayroot=tmpfs" $CMDLINE ; then
    sed -i $CMDLINE -e "s/^/overlayroot=tmpfs /"
  fi

  if [ "$BOOTRO" = "yes" ] ; then
    if ! mount -o remount,ro /boot${FIRMWARE} 2>/dev/null ; then
      echo "Unable to remount boot partition as read-only"
    fi
  fi
}

disable_overlayfs() {
  # mount the boot partition as writable if it isn't already
  if [ $(get_bootro_now) -eq 0 ] ; then
    if ! mount -o remount,rw /boot${FIRMWARE} 2>/dev/null ; then
      echo "Unable to mount boot partition as writable - cannot disable"
      return 1
    fi
    BOOTRO=yes
  else
    BOOTRO=no
  fi

  # modify command line
  sed -i $CMDLINE -e "s/\(.*\)overlayroot=tmpfs \(.*\)/\1\2/"

  if [ "$BOOTRO" = "yes" ] ; then
    if ! mount -o remount,ro /boot${FIRMWARE} 2>/dev/null ; then
      echo "Unable to remount boot partition as read-only"
    fi
  fi
}

enable_bootro() {
  if [ $(get_overlay_now) -eq 0 ] ; then
    echo "Overlay in use; cannot update fstab"
    return 1
  fi
  sed -i /etc/fstab -e "s#\(.*/boot$FIRMWARE.*\)defaults\(.*\)#\1defaults,ro\2#"
}

disable_bootro() {
  if [ $(get_overlay_now) -eq 0 ] ; then
    echo "Overlay in use; cannot update fstab"
    return 1
  fi
  sed -i /etc/fstab -e "s#\(.*/boot$FIRMWARE.*\)defaults,ro\(.*\)#\1defaults\2#"
}

do_overlayfs() {
  DEFAULT=--defaultno
  CURRENT=0
  STATUS="disabled"

  if [ "$INTERACTIVE" = True ] && ! is_uname_current; then
    whiptail --msgbox "Could not find modules for the running kernel ($(uname -r))." 20 60 1
    return 1
  fi

  if [ $(get_overlay_conf) -eq 0 ] ; then
    DEFAULT=
    CURRENT=1
    STATUS="enabled"
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like the overlay file system to be enabled?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq $CURRENT ]; then
    if [ $RET -eq 0 ]; then
      if enable_overlayfs; then
        STATUS="enabled"
        ASK_TO_REBOOT=1
      else
        STATUS="unchanged"
      fi
    elif [ $RET -eq 1 ]; then
      if disable_overlayfs; then
        STATUS="disabled"
        ASK_TO_REBOOT=1
      else
        STATUS="unchanged"
      fi
    else
      return $RET
    fi
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The overlay file system is $STATUS." 20 60 1
  fi
  if [ $(get_overlay_now) -eq 0 ] ; then
    if [ $(get_bootro_conf) -eq 0 ] ; then
      BPRO="read-only"
    else
      BPRO="writable"
    fi
    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "The boot partition is currently $BPRO. This cannot be changed while an overlay file system is enabled." 20 60 1
    fi
  else
    DEFAULT=--defaultno
    CURRENT=0
    STATUS="writable"
    if [ $(get_bootro_conf) -eq 0 ]; then
      DEFAULT=
      CURRENT=1
      STATUS="read-only"
    fi
    if [ "$INTERACTIVE" = True ]; then
      whiptail --yesno "Would you like the boot partition to be write-protected?" $DEFAULT 20 60 2
      RET=$?
    else
      RET=$1
    fi
    if [ $RET -eq $CURRENT ]; then
      if [ $RET -eq 0 ]; then
        if enable_bootro; then
          STATUS="read-only"
          ASK_TO_REBOOT=1
        else
          STATUS="unchanged"
        fi
      elif [ $RET -eq 1 ]; then
        if disable_bootro; then
          STATUS="writable"
          ASK_TO_REBOOT=1
        else
          STATUS="unchanged"
        fi
      else
        return $RET
      fi
    fi
    if [ "$INTERACTIVE" = True ]; then
      whiptail --msgbox "The boot partition is $STATUS." 20 60 1
    fi
  fi
}

get_proxy() {
  SCHEME="$1"
  VAR_NAME="${SCHEME}_proxy"
  if [ -f /etc/profile.d/proxy.sh ]; then
    # shellcheck disable=SC1091
    . /etc/profile.d/proxy.sh
  fi
  eval "echo \$$VAR_NAME"
}

do_proxy() {
  SCHEMES="$1"
  ADDRESS="$2"
  if [ "$SCHEMES" = "all" ]; then
    CURRENT="$(get_proxy http)"
    SCHEMES="http https ftp rsync"
  else
    CURRENT="$(get_proxy "$SCHEMES")"
  fi
  if [ "$INTERACTIVE" = True ]; then
    if [ "$SCHEMES" = "no" ]; then
      STRING="Please enter a comma separated list of addresses that should be excluded from using proxy servers.\\nEg: localhost,127.0.0.1,localaddress,.localdomain.com"
    else
      STRING="Please enter proxy address.\\nEg: http://user:pass@proxy:8080"
    fi
    if ! ADDRESS="$(whiptail --inputbox "$STRING"  20 60 "$CURRENT" 3>&1 1>&2 2>&3)"; then
      return 0
    fi
  fi
  for SCHEME in $SCHEMES; do
    unset "${SCHEME}_proxy"
    CURRENT="$(get_proxy "$SCHEME")"
    if [ "$CURRENT" != "$ADDRESS" ]; then
      ASK_TO_REBOOT=1
    fi
    if [ -f /etc/profile.d/proxy.sh ]; then
      sed -i "/^export ${SCHEME}_/Id" /etc/profile.d/proxy.sh
    fi
    if [ "${SCHEME#*http}" != "$SCHEME" ]; then
      if [ -f /etc/apt/apt.conf.d/01proxy ]; then
        sed -i "/::${SCHEME}::Proxy/d" /etc/apt/apt.conf.d/01proxy
      fi
    fi
    if [ -z "$ADDRESS" ]; then
      STATUS=cleared
      continue
    fi
    STATUS=updated
    SCHEME_UPPER="$(echo "$SCHEME" | tr '[:lower:]' '[:upper:]')"
    echo "export ${SCHEME_UPPER}_PROXY=\"$ADDRESS\"" >> /etc/profile.d/proxy.sh
    if [ "$SCHEME" != "rsync" ]; then
      echo "export ${SCHEME}_proxy=\"$ADDRESS\"" >> /etc/profile.d/proxy.sh
    fi
    if [ "${SCHEME#*http}" != "$SCHEME" ]; then
      echo "Acquire::$SCHEME::Proxy \"$ADDRESS\";"  >> /etc/apt/apt.conf.d/01proxy
    fi
  done
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "Proxy settings $STATUS" 20 60 1
  fi
}

get_usb_current() {
  USB=$(get_config_var usb_max_current_enable $CONFIG)
  if [ $USB -eq 1 ]; then
    echo 0
  else
    echo 1
  fi
}

do_usb_current() {
  DEFAULT=--defaultno
  if [ $(get_usb_current) -eq 0 ]; then
    DEFAULT=
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --yesno "Would you like the USB current limit to be disabled?" $DEFAULT 20 60 2
    RET=$?
  else
    RET=$1
  fi
  if [ $RET -eq 0 ]; then
    set_config_var usb_max_current_enable 1 $CONFIG &&
    STATUS=disabled
  elif [ $RET -eq 1 ]; then
    sed $CONFIG -i -e "/usb_max_current_enable.*/d"
    STATUS=enabled
  else
    return $RET
  fi


  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The USB current limit is $STATUS" 20 60 1
  fi
}

get_squeekboard() {
  if ! is_installed squeekboard ; then
    echo 2
  elif [ -e /etc/xdg/autostart/squeekboard.desktop ] ; then
    if grep -q sbtest /etc/xdg/autostart/squeekboard.desktop ; then
      echo 1
    else
      echo 0
    fi
  else
    echo 2
  fi
}

do_squeekboard() {
  if [ "$INTERACTIVE" = True ]; then
    OPT=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "On-screen Keyboard" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
      "S1 Always On" "On-screen keyboard always enabled" \
      "S2 Autodetect" "On-screen keyboard enabled if touch device found" \
      "S3 Always Off" "On-screen keyboard disabled" \
      3>&1 1>&2 2>&3)
  else
    OPT=$1
    true
  fi
  GFILE="/etc/xdg/labwc-greeter/autostart"
  if [ ! -e $GFILE ] ; then
    GFILE="/usr/share/labwc/autostart"
  fi
  case "$OPT" in
    S1*)
      is_installed squeekboard || pacman -S --noconfirm -y squeekboard
      cat > /etc/xdg/autostart/squeekboard.desktop << EOF
[Desktop Entry]
Name=Squeekboard
Comment=Launch the on-screen keyboard
Exec=/usr/bin/sbout
Terminal=false
Type=Application
NoDisplay=true
EOF
      sed -i '/sbtest/d' $GFILE
      if ! grep -q sbout $GFILE ; then
        echo "/usr/bin/sbout &" >> $GFILE
      fi
      PREFIX=""
      if [ -n "$SUDO_USER" ] ; then
        PREFIX="sudo -u $USER XDG_RUNTIME_DIR=/run/user/$SUDO_UID DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$SUDO_UID/bus "
      fi
      $PREFIX sbout > /dev/null 2> /dev/null &
      STATUS="enabled"
      ;;
    S2*)
      is_installed squeekboard || pacman -S --noconfirm -y squeekboard
      cat > /etc/xdg/autostart/squeekboard.desktop << EOF
[Desktop Entry]
Name=Squeekboard
Comment=Launch the on-screen keyboard
Exec=/usr/bin/sbtest
Terminal=false
Type=Application
NoDisplay=true
EOF
      sed -i '/sbout/d' $GFILE
      if ! grep -q sbtest $GFILE ; then
        echo "/usr/bin/sbtest &" >> $GFILE
      fi
      PREFIX=""
      if [ -n "$SUDO_USER" ] ; then
        PREFIX="sudo -u $USER XDG_RUNTIME_DIR=/run/user/$SUDO_UID DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$SUDO_UID/bus "
      fi
      pkill squeekboard
      $PREFIX sbtest > /dev/null 2> /dev/null &
      STATUS="using autodetect"
      ;;
    S3*)
      pkill squeekboard
      rm -f /etc/xdg/autostart/squeekboard.desktop
      sed -i '/sbout/d' $GFILE
      sed -i '/sbtest/d' $GFILE
      STATUS="disabled"
      ;;
    *)
      return $OPT
      ;;
  esac
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The onscreen keyboard is $STATUS" 20 60 1
  fi
}

get_squeek_output (){
  if [ -e /usr/share/squeekboard/output ] ; then
    echo `grep SQUEEKBOARD_PREFERRED_OUTPUT /usr/share/squeekboard/output | cut -d = -f 2`
  else
    echo ""
  fi
}

do_squeek_output() {
  PREFIX=""
  if [ -n "$SUDO_USER" ] ; then
    PREFIX="sudo -u $USER XDG_RUNTIME_DIR=/run/user/$SUDO_UID DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$SUDO_UID/bus "
  fi
  if [ "$INTERACTIVE" = True ]; then
    menu=$($PREFIX wlr-randr | grep -v ^' ' | cut -d ' ' -f 1 | tr '\n' '/' | sed 's#/#//#g')
    oIFS="$IFS"
    IFS="/"
    OPT=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Select where the on-screen keyboard is to be shown" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \
      ${menu} \
      3>&1 1>&2 2>&3)
    IFS="$oIFS"
  else
    OPT=$1
    true
  fi
  mkdir -p /usr/share/squeekboard/
  echo "#!/bin/sh\nexport SQUEEKBOARD_PREFERRED_OUTPUT=$OPT" > /usr/share/squeekboard/output
  chmod a+x /usr/share/squeekboard/output
  if pgrep sbtest > /dev/null ; then
    pkill squeekboard
    $PREFIX sbtest > /dev/null 2> /dev/null &
  elif pgrep squeekboard > /dev/null ; then
    pkill squeekboard
    $PREFIX sbout > /dev/null 2> /dev/null &
  fi
  if [ "$INTERACTIVE" = True ]; then
    whiptail --msgbox "The onscreen keyboard is on $OPT" 20 60 1
  fi
}

nonint() {
  "$@"
}

#
# Command line options for non-interactive use
#
for i in $*
do
  case $i in
  --memory-split)
    OPT_MEMORY_SPLIT=GET
    printf "Not currently supported\n"
    exit 1
    ;;
  --memory-split=*)
    OPT_MEMORY_SPLIT=$(echo $i | sed 's/[-a-zA-Z0-9]*=//')
    printf "Not currently supported\n"
    exit 1
    ;;
  --expand-rootfs)
    INTERACTIVE=False
    do_expand_rootfs
    printf "Please reboot\n"
    exit 0
    ;;
  --apply-os-config)
    INTERACTIVE=False
    do_apply_os_config
    exit $?
    ;;
  nonint)
    INTERACTIVE=False
    #echo "$@"
    "$@"
    exit $?
    ;;
  *)
    # unknown option
    ;;
  esac
done

#if [ "GET" = "${OPT_MEMORY_SPLIT:-}" ]; then
#  set -u # Fail on unset variables
#  get_current_memory_split
#  echo $CURRENT_MEMSPLIT
#  exit 0
#fi

# Everything else needs to be run as root
if [ $(id -u) -ne 0 ]; then
  printf "Script must be run as root. Try 'sudo parch-config'\n"
  exit 1
fi

if [ -n "${OPT_MEMORY_SPLIT:-}" ]; then
  set -e # Fail when a command errors
  set_memory_split "${OPT_MEMORY_SPLIT}"
  exit 0
fi

do_system_menu() {
  if is_pi ; then
    FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "System Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
      "S1 Wireless LAN" "Enter SSID and passphrase" \
      "S2 Audio" "Select audio out through HDMI or 3.5mm jack" \
      "S3 Password" "Change password for the '$USER' user" \
      "S4 Hostname" "Set name for this computer on a network" \
      "S5 Boot" "Select boot into desktop or to command line" \
      "S6 Auto Login" "Enable auto login to desktop or to command line" \
      "S7 Splash Screen" "Choose graphical splash screen or text boot" \
      "S8 Power LED" "Set behaviour of power LED" \
      "S9 Browser" "Choose default web browser" \
      3>&1 1>&2 2>&3)
  elif is_live ; then
    FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "System Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
      "S1 Wireless LAN" "Enter SSID and passphrase" \
      "S3 Password" "Change password for the '$USER' user" \
      "S4 Hostname" "Set name for this computer on a network" \
      "S5 Boot" "Select boot into desktop or to command line" \
      "S6 Auto Login" "Enable auto login to desktop or to command line" \
      3>&1 1>&2 2>&3)
  else
    FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "System Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
      "S1 Wireless LAN" "Enter SSID and passphrase" \
      "S3 Password" "Change password for the '$USER' user" \
      "S4 Hostname" "Set name for this computer on a network" \
      "S5 Boot" "Select boot into desktop or to command line" \
      "S6 Auto Login" "Enable auto login to desktop or to command line" \
      "S7 Splash Screen" "Choose graphical splash screen or text boot" \
      3>&1 1>&2 2>&3)
  fi
  RET=$?
  if [ $RET -eq 1 ]; then
    return 0
  elif [ $RET -eq 0 ]; then
    case "$FUN" in
      S1\ *) do_wifi_ssid_passphrase ;;
      S2\ *) do_audio ;;
      S3\ *) do_change_pass ;;
      S4\ *) do_hostname ;;
      S5\ *) do_boot_target ;;
      S6\ *) do_autologin ;;
      S7\ *) do_boot_splash ;;
      S8\ *) do_leds ;;
      S9\ *) do_browser ;;
      *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
    esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
  fi
}

do_display_menu() {
  if is_pi ; then
    if is_wayland; then
      if is_pifour; then
        FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Display Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
          "D2 Screen Blanking" "Enable/disable screen blanking" \
          "D4 Composite" "Enable/disable composite output" \
          "D5 4Kp60 HDMI" "Enable 4Kp60 resolution on HDMI0" \
          "D6 Onscreen Keyboard" "Enable on-screen keyboard" \
          "D7 Keyboard Output" "Select monitor used for on-screen keyboard" \
          3>&1 1>&2 2>&3)
      else
        FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Display Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
          "D2 Screen Blanking" "Enable/disable screen blanking" \
          "D4 Composite" "Enable/disable composite output" \
          "D6 Onscreen Keyboard" "Enable on-screen keyboard" \
          "D7 Keyboard Output" "Select monitor used for on-screen keyboard" \
          3>&1 1>&2 2>&3)
      fi
    else
      if is_pifour; then
        FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Display Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
          "D1 Underscan" "Remove black border around screen" \
          "D2 Screen Blanking" "Enable/disable screen blanking" \
          "D3 VNC Resolution" "Set resolution for headless use" \
          "D4 Composite" "Enable/disable composite output" \
          "D5 4Kp60 HDMI" "Enable 4Kp60 resolution on HDMI0" \
          3>&1 1>&2 2>&3)
      else
        FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Display Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
          "D1 Underscan" "Remove black border around screen" \
          "D2 Screen Blanking" "Enable/disable screen blanking" \
          "D3 VNC Resolution" "Set resolution for headless use" \
          "D4 Composite" "Enable/disable composite output" \
          3>&1 1>&2 2>&3)
      fi
    fi
  else
    FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Display Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
      "D1 Underscan" "Remove black border around screen" \
      "D2 Screen Blanking" "Enable/disable screen blanking" \
      3>&1 1>&2 2>&3)
  fi
  RET=$?
  if [ $RET -eq 1 ]; then
    return 0
  elif [ $RET -eq 0 ]; then
    case "$FUN" in
      D1\ *) do_overscan_kms ;;
      D2\ *) do_blanking ;;
      D3\ *) do_vnc_resolution ;;
      D4\ *) do_composite ;;
      D5\ *) do_pi4video ;;
      D6\ *) do_squeekboard ;;
      D7\ *) do_squeek_output ;;
      *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
    esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
  fi
}

do_interface_menu() {
  if is_pi ; then
    FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Interfacing Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
      "I1 SSH" "Enable/disable remote command line access using SSH" \
      "I2 RPi Connect" "Enable/disable Raspberry Pi Connect" \
      "I3 VNC" "Enable/disable graphical remote desktop access" \
      "I4 SPI" "Enable/disable automatic loading of SPI kernel module" \
      "I5 I2C" "Enable/disable automatic loading of I2C kernel module" \
      "I6 Serial Port" "Enable/disable shell messages on the serial connection" \
      "I7 1-Wire" "Enable/disable one-wire interface" \
      3>&1 1>&2 2>&3)
  else
    FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Interfacing Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
      "I2 SSH" "Enable/disable remote command line access using SSH" \
      3>&1 1>&2 2>&3)
  fi
  RET=$?
  if [ $RET -eq 1 ]; then
    return 0
  elif [ $RET -eq 0 ]; then
    case "$FUN" in
      I1\ *) do_ssh ;;
      I2\ *) do_rpi_connect ;;
      I3\ *) do_vnc ;;
      I4\ *) do_spi ;;
      I5\ *) do_i2c ;;
      I6\ *) if is_pifive ; then do_serial_pi5 ; else do_serial ; fi ;;
      I7\ *) do_onewire ;;
      *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
    esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
  fi
}

do_performance_menu() {
  case "$(get_pi_type)" in
    [03]) FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Performance Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
          "P2 Overlay File System" "Enable/disable read-only file system" \
          3>&1 1>&2 2>&3) ;;
    [12]) FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Performance Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
          "P1 Overclock" "Configure CPU overclocking" \
          "P2 Overlay File System" "Enable/disable read-only file system" \
          3>&1 1>&2 2>&3) ;;
    4) FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Performance Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
          "P2 Overlay File System" "Enable/disable read-only file system" \
          "P3 Fan" "Set behaviour of GPIO case fan" \
          3>&1 1>&2 2>&3) ;;
    *) FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Performance Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
          "P2 Overlay File System" "Enable/disable read-only file system" \
          "P4 USB Current" "Set USB current limit" \
          3>&1 1>&2 2>&3) ;;
  esac
  RET=$?
  if [ $RET -eq 1 ]; then
    return 0
  elif [ $RET -eq 0 ]; then
    case "$FUN" in
      P1\ *) do_overclock ;;
      P2\ *) do_overlayfs ;;
      P3\ *) do_fan ;;
      P4\ *) do_usb_current ;;
      *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
    esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
  fi
}

do_internationalisation_menu() {
  FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Localisation Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
    "L1 Locale" "Configure language and regional settings" \
    "L2 Timezone" "Configure time zone" \
    "L3 Keyboard" "Set keyboard layout to match your keyboard" \
    "L4 WLAN Country" "Set legal wireless channels for your country" \
    3>&1 1>&2 2>&3)
  RET=$?
  if [ $RET -eq 1 ]; then
    return 0
  elif [ $RET -eq 0 ]; then
    case "$FUN" in
      L1\ *) do_change_locale ;;
      L2\ *) do_change_timezone ;;
      L3\ *) do_configure_keyboard ;;
      L4\ *) do_wifi_country ;;
      *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
    esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
  fi
}

do_advanced_menu() {
  if is_pifive ; then
    FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Advanced Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
      "A1 Expand Filesystem" "Ensures that all of the SD card is available" \
      "A2 Network Interface Names" "Enable/disable predictable network i/f names" \
      "A3 Network Proxy Settings" "Configure network proxy settings" \
      "A4 Boot Order" "Choose boot device priority (SD/net/USB/NVMe)" \
      "A5 Bootloader Version" "Selects the latest or the factory default bootloader" \
      "A6 Beta Access" "Select beta or release software repository" \
      "A7 Wayland" "Switch between X and Wayland backends" \
      "A8 PCIe Speed" "Set PCIe x1 port speed" \
      "A9 Network Install UI" "Select display of bootloader network install UI" \
      "A10 Libliftoff" "Enable/disable libliftoff hardware overlays" \
      "A11 Shutdown Behaviour" "Configure shutdown behavior" \
      "A12 Logging" "Set storage location for logs" \
      "A13 WLAN Power Save" "Enable/disable WLAN power saving" \
      "A14 Link-local Fallback" "Enable/disable link-local address fallback" \
      3>&1 1>&2 2>&3)
  elif is_pifour ; then
    FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Advanced Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
      "A1 Expand Filesystem" "Ensures that all of the SD card is available" \
      "A2 Network Interface Names" "Enable/disable predictable network i/f names" \
      "A3 Network Proxy Settings" "Configure network proxy settings" \
      "A4 Boot Order" "Choose boot device priority (SD/net/USB/NVMe)" \
      "A5 Bootloader Version" "Selects the latest or the factory default bootloader" \
      "A6 Beta Access" "Select beta or release software repository" \
      "A7 Wayland" "Switch between X and Wayland backends" \
      "A9 Network Install UI" "Select display of bootloader network install UI" \
      "A10 Libliftoff" "Enable/disable libliftoff hardware overlays" \
      "A11 Shutdown Behaviour" "Configure shutdown behavior" \
      "A12 Logging" "Set storage location for logs" \
      "A13 WLAN Power Save" "Enable/disable WLAN power saving" \
      "A14 Link-local Fallback" "Enable/disable link-local address fallback" \
      3>&1 1>&2 2>&3)
  elif is_pi ; then
    FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Advanced Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
      "A1 Expand Filesystem" "Ensures that all of the SD card is available" \
      "A2 Network Interface Names" "Enable/disable predictable network i/f names" \
      "A3 Network Proxy Settings" "Configure network proxy settings" \
      "A6 Beta Access" "Select beta or release software repository" \
      "A7 Wayland" "Switch between X and Wayland backends" \
      "A10 Libliftoff" "Enable/disable libliftoff hardware overlays" \
      "A12 Logging" "Set storage location for logs" \
      "A13 WLAN Power Save" "Enable/disable WLAN power saving" \
      "A14 Link-local Fallback" "Enable/disable link-local address fallback" \
      3>&1 1>&2 2>&3)
  else
    FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Advanced Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
      "A2 Network Interface Names" "Enable/disable predictable network i/f names" \
      "A3 Network Proxy Settings" "Configure network proxy settings" \
      "A6 Beta Access" "Select beta or release software repository" \
      "A7 Wayland" "Switch between X and Wayland backends" \
      "A10 Libliftoff" "Enable/disable libliftoff hardware overlays" \
      "A12 Logging" "Set storage location for logs" \
      "A13 WLAN Power Save" "Enable/disable WLAN power saving" \
      "A14 Link-local Fallback" "Enable/disable link-local address fallback" \
      3>&1 1>&2 2>&3)
  fi
  RET=$?
  if [ $RET -eq 1 ]; then
    return 0
  elif [ $RET -eq 0 ]; then
    case "$FUN" in
      A1\ *) do_expand_rootfs ;;
      A2\ *) do_net_names ;;
      A3\ *) do_proxy_menu ;;
      A4\ *) do_boot_order ;;
      A5\ *) do_boot_rom ;;
      A6\ *) do_beta_mode ;;
      A7\ *) do_wayland ;;
      A8\ *) do_pci ;;
      A9\ *) do_network_install_ui ;;
      A10\ *) do_libliftoff ;;
      A11\ *) do_power_off_on_halt ;;
      A12\ *) do_journald_storage ;;
      A13\ *) do_wifi_power_save ;;
      A14\ *) do_link_local_fallback ;;
      *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
    esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
  fi
}

do_proxy_menu() {
  FUN=$(whiptail --title "Parch Configuration Tool (parch-config)" --menu "Network Proxy Settings" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
    "P1 All" "Set the same proxy for all schemes" \
    "P2 HTTP" "Set the HTTP proxy" \
    "P3 HTTPS" "Set the HTTPS/SSL proxy" \
    "P4 FTP" "Set the FTP proxy" \
    "P5 RSYNC" "Set the RSYNC proxy" \
    "P6 Exceptions" "Set addresses for which a proxy server should not be used" \
    3>&1 1>&2 2>&3)
  RET=$?
  if [ $RET -eq 1 ]; then
    return 0
  elif [ $RET -eq 0 ]; then
    case "$FUN" in
      P1\ *) do_proxy all ;;
      P2\ *) do_proxy http ;;
      P3\ *) do_proxy https ;;
      P4\ *) do_proxy ftp ;;
      P5\ *) do_proxy rsync ;;
      P6\ *) do_proxy no;;
      *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
    esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
  fi
}

get_display_manager() {
    if systemctl is-active --quiet sddm 2>/dev/null; then
        echo "sddm"
    elif systemctl is-active --quiet gdm 2>/dev/null; then
        echo "gdm"
    elif systemctl is-active --quiet lightdm 2>/dev/null; then
        echo "lightdm"
    elif systemctl is-active --quiet cosmic-greeter 2>/dev/null; then
        echo "cosmic-greeter"
    elif systemctl is-active --quiet ly 2>/dev/null; then
        echo "ly"
    elif systemctl is-active --quiet greetd 2>/dev/null; then
        echo "greetd"
    else
        echo "none"
    fi
}

get_desktop_environment() {
    if pgrep -x "gnome-shell" > /dev/null 2>&1; then
        echo "gnome"
    elif pgrep -x "plasma-desktop" > /dev/null 2>&1 || pgrep -x "plasmashell" > /dev/null 2>&1; then
        echo "kde"
    elif pgrep -x "xfce4-session" > /dev/null 2>&1; then
        echo "xfce"
    elif pgrep -x "mate-session" > /dev/null 2>&1; then
        echo "mate"
    elif pgrep -x "cinnamon-session" > /dev/null 2>&1; then
        echo "cinnamon"
    elif pgrep -x "wayfire" > /dev/null 2>&1; then
        echo "wayfire"
    elif pgrep -x "labwc" > /dev/null 2>&1; then
        echo "labwc"
    elif pgrep -x "sway" > /dev/null 2>&1; then
        echo "sway"
    elif pgrep -x "cosmic-_applet" > /dev/null 2>&1; then
        echo "cosmic"
    else
        echo "unknown"
    fi
}

get_gpu_info() {
    if lsmod | grep -q "^nvidia " 2>/dev/null; then
        echo "nvidia"
    elif [ -f /usr/bin/nvidia-smi ]; then
        echo "nvidia"
    elif [ -f /sys/class/drm/card0/device/vendor ]; then
        VENDOR=$(cat /sys/class/drm/card0/device/vendor)
        case "$VENDOR" in
            0x10de) echo "nvidia" ;;
            0x1002) echo "amd" ;;
            0x8086) echo "intel" ;;
            *) echo "unknown" ;;
        esac
    else
        echo "none"
    fi
}

do_parch_desktop_menu() {
    DM=$(get_display_manager)
    DE=$(get_desktop_environment)
    GPU=$(get_gpu_info)
    
    if is_wsl; then
        do_wsl_menu
        return
    fi
    
    FUN=$(whiptail --title "Parch Configuration Tool (parch-config)" --menu "Desktop Settings" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "D1 GPU Drivers" "Configure GPU drivers (Current: $GPU)" \
        "D2 Display Manager" "Switch display manager (Current: $DM)" \
        "D3 Desktop Env" "Desktop Environment (Current: $DE)" \
        "D4 Screen Blanking" "Configure screen blanking/DPMS" \
        "D5 Resolution" "Change display resolution" \
        "D6 Boot to Desktop" "Enable/disable graphical boot" \
        "D7 Auto Login" "Configure automatic login" \
        "D8 Splash" "Plymouth boot splash screen" \
        "D9 compositor" "Configure compositor (picom/compton)" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            D1\ *) do_gpu_drivers ;;
            D2\ *) do_display_manager_switch ;;
            D3\ *) do_desktop_info ;;
            D4\ *) do_screen_blanking ;;
            D5\ *) do_display_resolution ;;
            D6\ *) do_boot_to_desktop ;;
            D7\ *) do_autologin ;;
            D8\ *) do_plymouth_splash ;;
            D9\ *) do_compositor_config ;;
            *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
        esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
    fi
}

do_gpu_drivers() {
    GPU=$(get_gpu_info)
    NVIDIA_INSTALLED=$(check_nvidia_installed)
    
    if [ "$NVIDIA_INSTALLED" = "yes" ]; then
        NVIDIA_STATUS="NVIDIA (installed)"
    else
        NVIDIA_STATUS="NVIDIA"
    fi
    
    FUN=$(whiptail --title "Parch Configuration Tool - GPU Drivers" --menu "Current GPU: $GPU" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "$NVIDIA_STATUS" "NVIDIA proprietary drivers" \
        "AMD" "AMD open source drivers (mesa)" \
        "Intel" "Intel open source drivers" \
        "Info" "Show detailed GPU info" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            NVIDIA*)
                if [ "$NVIDIA_INSTALLED" = "yes" ]; then
                    FUN2=$(whiptail --title "NVIDIA" --menu "NVIDIA is installed" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
                        "Uninstall" "Remove NVIDIA drivers" \
                        "Settings" "Open nvidia-settings" \
                        "Info" "NVIDIA driver info" \
                        3>&1 1>&2 2>&3)
                    RET2=$?
                    if [ $RET2 -eq 1 ]; then
                        return 0
                    elif [ $RET2 -eq 0 ]; then
                        case "$FUN2" in
                            Uninstall)
                                if [ "$INTERACTIVE" = True ]; then
                                    whiptail --yesno "Remove NVIDIA drivers?\nThis will also remove steam and some games." --defaultno 20 60 2
                                    RET3=$?
                                else
                                    RET3=1
                                fi
                                if [ $RET3 -eq 0 ]; then
                                    pacman -Rns nvidia nvidia-utils nvidia-settings 2>/dev/null
                                    whiptail --msgbox "NVIDIA drivers removed" 20 60 1
                                fi
                                ;;
                            Settings)
                                nvidia-settings 2>/dev/null &
                                ;;
                            Info)
                                INFO=$(nvidia-smi 2>/dev/null || echo "nvidia-smi not available")
                                whiptail --msgbox "$INFO" 25 70 1
                                ;;
                        esac
                    fi
                else
                    if [ "$INTERACTIVE" = True ]; then
                        whiptail --yesno "Install NVIDIA drivers?\nThis may take a while..." --defaultno 20 60 2
                        RET2=$?
                    else
                        RET2=1
                    fi
                    if [ $RET2 -eq 0 ]; then
                        pacman -S --noconfirm nvidia nvidia-utils nvidia-settings 2>/dev/null
                        ASK_TO_REBOOT=1
                        whiptail --msgbox "NVIDIA drivers installed.\nReboot required." 20 60 1
                    fi
                fi
                ;;
            AMD)
                if [ "$INTERACTIVE" = True ]; then
                    whiptail --msgbox "AMD Setup:\n\nInstall: sudo pacman -S mesa vulkan-radeon libva-mesa-driver\n\nFor Wayland: sudo pacman -S amdvlk" 20 60 1
                fi
                ;;
            Intel)
                if [ "$INTERACTIVE" = True ]; then
                    whiptail --msgbox "Intel Setup:\n\nInstall: sudo pacman -S mesa intel-media-driver\n\nFor VA-API: sudo pacman -S libva-intel-driver" 20 60 1
                fi
                ;;
            Info)
                if command -v inxi >/dev/null 2>&1; then
                    INFO=$(inxi -G)
                elif [ -f /sys/class/drm/card0/device/vendor ]; then
                    INFO="Vendor: $(cat /sys/class/drm/card0/device/vendor)\nDriver: $(cat /sys/class/drm/card0/device/driver 2>/dev/null || echo 'unknown')"
                else
                    INFO="No GPU detected"
                fi
                if [ "$NVIDIA_INSTALLED" = "yes" ]; then
                    INFO="$INFO\n\nNVIDIA Status: Installed"
                fi
                whiptail --msgbox "GPU Information:\n\n$INFO" 20 60 1
                ;;
        esac
    fi
}

do_display_manager_switch() {
    FUN=$(whiptail --title "Parch Configuration Tool - Display Manager" --menu "Select Display Manager to Enable" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "SDDM" "Simple Desktop Display Manager" \
        "GDM" "GNOME Display Manager" \
        "LightDM" "Lightweight Display Manager" \
        "Ly" "Terminal-based DM" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        DM_PACKAGE=""
        case "$FUN" in
            SDDM) DM_PACKAGE="sddm" ;;
            GDM) DM_PACKAGE="gdm" ;;
            LightDM) DM_PACKAGE="lightdm" ;;
            Ly) DM_PACKAGE="ly" ;;
        esac
        
        if ! is_installed "$DM_PACKAGE"; then
            if [ "$INTERACTIVE" = True ]; then
                whiptail --yesno "$DM_PACKAGE is not installed.\nInstall now?" --defaultno 20 60 2
                RET2=$?
            else
                RET2=1
            fi
            if [ $RET2 -eq 0 ]; then
                pacman -S --noconfirm "$DM_PACKAGE" 2>/dev/null
            fi
        fi
        
        systemctl disable sddm gdm lightdm ly 2>/dev/null || true
        systemctl enable "$DM_PACKAGE"
        
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "Display manager set to $FUN" 20 60 1
        fi
    fi
}

do_desktop_info() {
    DE=$(get_desktop_environment)
    FUN=$(whiptail --title "Parch Configuration Tool - Desktop Environment" --menu "Current DE: $DE" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "KDE Plasma" "Install/configure KDE Plasma" \
        "GNOME" "Install/configure GNOME" \
        "XFCE" "Install/configure XFCE" \
        "MATE" "Install/configure MATE" \
        "Cinnamon" "Install/configure Cinnamon" \
        "Wayfire" "Install/configure Wayfire (Wayland)" \
        "Sway" "Install/configure Sway (Wayland)" \
        "Cosmic" "Install/configure System76 Cosmic" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            "KDE Plasma") PKG="plasma plasma-wayland-session" ;;
            "GNOME") PKG="gnome gnome-extra" ;;
            "XFCE") PKG="xfce4 xfce4-goodies" ;;
            "MATE") PKG="mate mate-extra" ;;
            "Cinnamon") PKG="cinnamon" ;;
            "Wayfire") PKG="wayfire" ;;
            "Sway") PKG="sway" ;;
            "Cosmic") PKG="cosmic" ;;
        esac
        
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "To install $FUN:\n\nsudo pacman -S $PKG\n\nThen enable display manager." 20 60 1
        fi
    fi
}

do_screen_blanking() {
    DE=$(get_desktop_environment)
    
    if [ "$INTERACTIVE" = True ]; then
        whiptail --yesno "Disable screen blanking?" --defaultno 20 60 2
        RET=$?
    else
        RET=$1
    fi
    
    if [ $RET -eq 0 ]; then
        if [ -d /etc/X11/xorg.conf.d ]; then
            cat > /etc/X11/xorg.conf.d/10-disable-blanking.conf << 'EOF'
Section "ServerLayout"
    Identifier "ServerLayout0"
    Option "StandbyTime" "0"
    Option "SuspendTime" "0"
    Option "OffTime" "0"
EndSection
EOF
        fi
        
        if [ "$DE" = "kde" ]; then
            mkdir -p ~/.config
            echo "[PowerDevil][PowerProfile][power profile policies]" >> ~/.config/kwinrc
            echo "screenAutomationEnabled=false" >> ~/.config/kwinrc
        fi
        
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "Screen blanking disabled" 20 60 1
        fi
    else
        rm -f /etc/X11/xorg.conf.d/10-disable-blanking.conf
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "Screen blanking enabled (restore default)" 20 60 1
        fi
    fi
}

do_display_resolution() {
    if ! command -v xrandr >/dev/null 2>&1; then
        whiptail --msgbox "xrandr not available.\nInstall xorg-xrandr" 20 60 1
        return 1
    fi
    
    RESOLUTIONS=$(xrandr 2>/dev/null | grep -E "^\s+[0-9]+x[0-9]+" | awk '{print $1}' | head -10)
    
    if [ -z "$RESOLUTIONS" ]; then
        whiptail --msgbox "Could not detect displays.\nMake sure X11 is running." 20 60 1
        return 1
    fi
    
    CURRENT=$(xrandr 2>/dev/null | grep -E "^\s+[0-9]+x[0-9]+" | grep -E "\*" | awk '{print $1}')
    
    if [ "$INTERACTIVE" = True ]; then
        RES=$(whiptail --title "Display Resolution" --menu "Current: $CURRENT" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select $RESOLUTIONS 3>&1 1>&2 2>&3)
        RET=$?
    else
        RES="$1"
        RET=0
    fi
    
    if [ $RET -eq  -n "$RES" ]; then
        xrandr -0 ] && [s "$RES" >/dev/null 2>&1
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "Resolution set to $RES" 20 60 1
        fi
    fi
}

do_compositor_config() {
    if is_installed picom; then
        COMPOSITOR="picom"
    elif is_installed compton; then
        COMPOSITOR="compton"
    else
        COMPOSITOR="none"
    fi
    
    FUN=$(whiptail --title "Parch Configuration Tool - Compositor" --menu "Current: $COMPOSITOR" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "Install Picom" "Install and configure picom" \
        "Enable" "Enable compositor" \
        "Disable" "Disable compositor" \
        "Config" "Edit compositor config" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            "Install Picom")
                pacman -S --noconfirm picom 2>/dev/null
                whiptail --msgbox "Picom installed.\nConfig: ~/.config/picom.conf" 20 60 1
                ;;
            "Enable")
                picom --experimental-backends &
                whiptail --msgbox "Compositor enabled" 20 60 1
                ;;
            "Disable")
                pkill picom 2>/dev/null || pkill compton 2>/dev/null
                whiptail --msgbox "Compositor disabled" 20 60 1
                ;;
            "Config")
                EDITOR=${EDITOR:-nano}
                $EDITOR ~/.config/picom.conf 2>/dev/null || whiptail --msgbox "No config found.\nCreate ~/.config/picom.conf" 20 60 1
                ;;
        esac
    fi
}

do_wsl_menu() {
    FUN=$(whiptail --title "Parch Configuration Tool - WSL Settings" --menu "WSL Configuration" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "W1 GPU" "Configure GPU acceleration (WSLg)" \
        "W2 Memory" "Configure WSL memory limit" \
        "W3 WSLg" "Enable/disable WSLg (GUI apps)" \
        "W4 Interop" "Enable Windows interop" \
        "W5 Mount" "Configure 9P mount options" \
        "W6 systemd" "Toggle systemd in WSL" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            W1\ *) do_wsl_gpu ;;
            W2\ *) do_wsl_memory ;;
            W3\ *) do_wslg ;;
            W4\ *) do_wsl_interop ;;
            W5\ *) do_wsl_mount ;;
            W6\ *) do_wsl_systemd ;;
            *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
        esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
    fi
}

do_wsl_gpu() {
    if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "WSL GPU Setup:\n\nFor NVIDIA:\n  Install drivers on Windows\n  wsl --update\n\nFor AMD/Intel:\n  Use WSLg (built into Windows 11)\n\nCheck: wsl -l -v" 20 60 1
    fi
}

do_wsl_memory() {
    CURRENT_MEM=$(grep -i memory /proc/meminfo | head -1 | awk '{print $2}')
    CURRENT_MEM_MB=$((CURRENT_MEM / 1024))
    
    if [ "$INTERACTIVE" = True ]; then
        NEW_MEM=$(whiptail --inputbox "Enter memory limit (e.g., 4GB, 8GB):" 20 60 "4GB" 3>&1 1>&2 2>&3)
        RET=$?
    else
        NEW_MEM="$1"
        RET=0
    fi
    
    if [ $RET -eq 0 ] && [ -n "$NEW_MEM" ]; then
        whiptail --msgbox "To set WSL memory:\n\nAdd to %USERPROFILE%\\.wslconfig:\n\n[wsl2]\nmemory=${NEW_MEM}\nprocessors=4\n\nThen run: wsl --shutdown" 20 60 1
    fi
}

do_wslg() {
    if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "WSLg is enabled by default in Windows 11.\n\nTo check:\nwsl -l -v\n\nTo enable manually:\nwsl --install --web-download\n\nMake sure WSL version is 2." 20 60 1
    fi
}

do_wsl_interop() {
    if grep -q "appendWindowsPath=true" /etc/wsl.conf 2>/dev/null; then
        CURRENT="enabled"
    else
        CURRENT="disabled"
    fi
    
    if [ "$INTERACTIVE" = True ]; then
        whiptail --yesno "Windows interop is $CURRENT.\nEnable for Windows PATH in WSL?" --defaultno 20 60 2
        RET=$?
    else
        RET=0
    fi
    
    if [ $RET -eq 0 ]; then
        cat > /etc/wsl.conf << 'EOF'
[interop]
enabled=true
appendWindowsPath=true
EOF
        whiptail --msgbox "Windows interop enabled.\nRestart WSL: wsl --shutdown" 20 60 1
    fi
}

do_wsl_mount() {
    if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "9P Mount Options:\n\nEdit /etc/wsl.conf:\n\n[automount]\nmountOptions=\"metadata,umask=22,fmask=11\"\n\nThen: wsl --shutdown" 20 60 1
    fi
}

do_wsl_systemd() {
    if grep -q "systemd=true" /etc/wsl.conf 2>/dev/null; then
        CURRENT="enabled"
    else
        CURRENT="disabled"
    fi
    
    if [ "$INTERACTIVE" = True ]; then
        whiptail --yesno "systemd is currently $CURRENT.\nToggle systemd?" --defaultno 20 60 2
        RET=$?
    else
        RET=0
    fi
    
    if [ $RET -eq 0 ]; then
        if grep -q "\[boot\]" /etc/wsl.conf 2>/dev/null; then
            sed -i 's/systemd=.*/systemd=true/' /etc/wsl.conf
        else
            echo "[boot]" >> /etc/wsl.conf
            echo "systemd=true" >> /etc/wsl.conf
        fi
        whiptail --msgbox "systemd configuration updated.\nRestart WSL: wsl --shutdown" 20 60 1
    fi
}

do_parch_services_menu() {
    if is_wsl; then
        do_wsl_menu
        return
    fi
    
    FUN=$(whiptail --title "Parch Configuration Tool (parch-config)" --menu "System Services & Server" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "S1 SSH" "SSH server (OpenSSH)" \
        "S2 Firewall" "UFW firewall configuration" \
        "S3 Docker" "Docker container runtime" \
        "S4 Network" "NetworkManager configuration" \
        "S5 Bluetooth" "Bluetooth service" \
        "S6 Cups" "Printing service" \
        "S7 SSH Hardening" "Secure SSH configuration" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            S1\ *) do_ssh ;;
            S2\ *) do_firewall_config ;;
            S3\ *) do_docker_config ;;
            S4\ *) do_network_config ;;
            S5\ *) do_bluetooth ;;
            S6\ *) do_cups ;;
            S7\ *) do_ssh_hardening ;;
            *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
        esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
    fi
}

do_firewall_config() {
    if ! is_installed ufw; then
        if [ "$INTERACTIVE" = True ]; then
            whiptail --yesno "UFW not installed.\nInstall now?" --defaultno 20 60 2
            RET=$?
        else
            RET=1
        fi
        if [ $RET -eq 0 ]; then
            pacman -S --noconfirm ufw 2>/dev/null
        fi
    fi
    
    FUN=$(whiptail --title "Parch Configuration Tool - Firewall" --menu "UFW Firewall Configuration" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "Enable" "Enable firewall (default: deny incoming)" \
        "Disable" "Disable firewall" \
        "Status" "Show firewall status" \
        "Common Ports" "Quick rules for common services" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            Enable)
                ufw default deny incoming
                ufw default allow outgoing
                ufw enable
                whiptail --msgbox "Firewall enabled.\nDefault: deny incoming, allow outgoing" 20 60 1
                ;;
            Disable)
                ufw disable
                whiptail --msgbox "Firewall disabled" 20 60 1
                ;;
            Status)
                STATUS=$(ufw status 2>/dev/null || echo "UFW not available")
                whiptail --msgbox "$STATUS" 20 60 1
                ;;
            "Common Ports")
                whiptail --msgbox "Common port rules:\n\nsudo ufw allow 80/tcp   # HTTP\nsudo ufw allow 443/tcp  # HTTPS\nsudo ufw allow 22/tcp   # SSH\nsudo ufw allow 8080/tcp # Custom" 20 60 1
                ;;
        esac
    fi
}

do_docker_config() {
    if ! is_installed docker; then
        if [ "$INTERACTIVE" = True ]; then
            whiptail --yesno "Docker not installed.\nInstall now?" --defaultno 20 60 2
            RET=$?
        else
            RET=1
        fi
        if [ $RET -eq 0 ]; then
            pacman -S --noconfirm docker 2>/dev/null
        fi
    fi
    
    FUN=$(whiptail --title "Parch Configuration Tool - Docker" --menu "Docker Configuration" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "Enable" "Enable and start Docker" \
        "Disable" "Disable Docker" \
        "Info" "Docker system info" \
        "Compose" "Docker Compose info" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            Enable)
                systemctl enable docker
                systemctl start docker
                usermod -aG docker "$USER"
                whiptail --msgbox "Docker enabled.\nUser added to docker group.\nLog out and back in to apply." 20 60 1
                ;;
            Disable)
                systemctl stop docker
                systemctl disable docker
                whiptail --msgbox "Docker disabled" 20 60 1
                ;;
            Info)
                if command -v docker >/dev/null 2>&1; then
                    INFO=$(docker info 2>/dev/null | head -20 || echo "Docker not running")
                else
                    INFO="Docker not installed"
                fi
                whiptail --msgbox "$INFO" 25 70 1
                ;;
            Compose)
                if command -v docker-compose >/dev/null 2>&1 || command -v docker compose >/dev/null 2>&1; then
                    whiptail --msgbox "Docker Compose is available.\n\nUse: docker compose up -d\nor: docker-compose up -d" 20 60 1
                else
                    whiptail --msgbox "Docker Compose not found.\n\nInstall: sudo pacman -S docker-compose" 20 60 1
                fi
                ;;
        esac
    fi
}

do_network_config() {
    if ! is_installed networkmanager; then
        whiptail --msgbox "NetworkManager not installed.\nInstall with: sudo pacman -S networkmanager" 20 60 1
        return 1
    fi
    
    FUN=$(whiptail --title "Parch Configuration Tool - Network" --menu "NetworkManager Configuration" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "Status" "Show network status" \
        "WiFi" "WiFi settings" \
        "Ethernet" "Ethernet settings" \
        "DNS" "Configure DNS servers" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            Status)
                STATUS=$(nmcli device status 2>/dev/null || echo "NetworkManager not running")
                whiptail --msgbox "$STATUS" 20 60 1
                ;;
            WiFi)
                if command -v nmtui >/dev/null 2>&1; then
                    whiptail --msgbox "Run: sudo nmtui\nOr: sudo nmcli device wifi connect SSID password PASSWORD" 20 60 1
                else
                    whiptail --msgbox "WiFi config:\n\nsudo nmcli device wifi connect SSID password PASSWORD" 20 60 1
                fi
                ;;
            Ethernet)
                whiptail --msgbox "Ethernet:\n\nAuto-detected via DHCP.\nFor static IP:\n\nsudo nmtui -> Edit connection" 20 60 1
                ;;
            DNS)
                CURRENT_DNS=$(grep "^nameserver" /etc/resolv.conf 2>/dev/null | head -1 | awk '{print $2}')
                if [ "$INTERACTIVE" = True ]; then
                    NEW_DNS=$(whiptail --inputbox "Enter DNS server (e.g., 1.1.1.1):" 20 60 "$CURRENT_DNS" 3>&1 1>&2 2>&3)
                    RET2=$?
                else
                    NEW_DNS="$1"
                    RET2=0
                fi
                if [ $RET2 -eq 0 ] && [ -n "$NEW_DNS" ]; then
                    mkdir -p /etc/systemd/resolved.conf.d
                    echo "[Resolve]" > /etc/systemd/resolved.conf.d/dns.conf
                    echo "DNS=$NEW_DNS" >> /etc/systemd/resolved.conf.d/dns.conf
                    systemctl restart systemd-resolved
                    whiptail --msgbox "DNS set to $NEW_DNS" 20 60 1
                fi
                ;;
        esac
    fi
}

do_ssh_hardening() {
    FUN=$(whiptail --title "Parch Configuration Tool - SSH Hardening" --menu "SSH Security Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "Disable Root" "Disable root login" \
        "Password Auth" "Disable password authentication" \
        "Key Auth" "Enable key-only authentication" \
        "Port Change" "Change SSH port" \
        "Fail2Ban" "Install/configure Fail2Ban" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            "Disable Root")
                sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
                systemctl restart sshd
                whiptail --msgbox "Root login disabled" 20 60 1
                ;;
            "Password Auth")
                sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
                systemctl restart sshd
                whiptail --msgbox "Password authentication disabled" 20 60 1
                ;;
            "Key Auth")
                sed -i 's/^#*PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
                sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
                systemctl restart sshd
                whiptail --msgbox "Key authentication enabled,\npassword disabled" 20 60 1
                ;;
            "Port Change")
                if [ "$INTERACTIVE" = True ]; then
                    NEW_PORT=$(whiptail --inputbox "Enter new SSH port:" 20 60 "2222" 3>&1 1>&2 2>&3)
                    RET2=$?
                else
                    NEW_PORT="$1"
                    RET2=0
                fi
                if [ $RET2 -eq 0 ] && [ -n "$NEW_PORT" ]; then
                    sed -i "s/^#*Port.*/Port $NEW_PORT/" /etc/ssh/sshd_config
                    systemctl restart sshd
                    whiptail --msgbox "SSH port changed to $NEW_PORT" 20 60 1
                fi
                ;;
            "Fail2Ban")
                if ! is_installed fail2ban; then
                    if [ "$INTERACTIVE" = True ]; then
                        whiptail --yesno "Install Fail2Ban?" --defaultno 20 60 2
                        RET2=$?
                    else
                        RET2=1
                    fi
                    if [ $RET2 -eq 0 ]; then
                        pacman -S --noconfirm fail2ban 2>/dev/null
                    fi
                fi
                if is_installed fail2ban; then
                    systemctl enable fail2ban
                    systemctl start fail2ban
                    whiptail --msgbox "Fail2Ban enabled" 20 60 1
                else
                    whiptail --msgbox "Fail2Ban not installed" 20 60 1
                fi
                ;;
        esac
    fi
}

do_parch_package_menu() {
    FUN=$(whiptail --title "Parch Configuration Tool (parch-config)" --menu "Package Manager" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "P1 Mirrorlist" "Rank fastest mirrors" \
        "P2 AUR Helper" "Configure AUR (paru/yay)" \
        "P3 Update Keys" "Update keyring" \
        "P4 Clean Cache" "Clean pacman cache" \
        "P5 Sync" "Sync package database" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            P1\ *) do_rank_mirrors ;;
            P2\ *) do_aur_helper_config ;;
            P3\ *) do_update_keyring ;;
            P4\ *) do_clean_cache ;;
            P5\ *) do_sync_packages ;;
            *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
        esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
    fi
}

do_rank_mirrors() {
    if ! is_installed reflector; then
        if [ "$INTERACTIVE" = True ]; then
            whiptail --yesno "Install reflector to rank mirrors?" --defaultno 20 60 2
            RET=$?
        else
            RET=1
        fi
        if [ $RET -eq 0 ]; then
            pacman -S --noconfirm reflector 2>/dev/null
        fi
    fi
    
    if is_installed reflector; then
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "Ranking mirrors...\n\nThis may take a few minutes." 20 60 1
        fi
        reflector --latest 20 --sort rate --save /etc/pacman.d/mirrorlist 2>/dev/null
        pacman -Syy 2>/dev/null
        whiptail --msgbox "Mirrors ranked by speed" 20 60 1
    else
        whiptail --msgbox "To rank mirrors:\n\nsudo pacman -S reflector\nsudo reflector --latest 20 --sort rate --save /etc/pacman.d/mirrorlist" 20 60 1
    fi
}

do_aur_helper_config() {
    if is_installed paru; then
        AUR="paru"
    elif is_installed yay; then
        AUR="yay"
    else
        AUR="none"
    fi
    
    FUN=$(whiptail --title "Parch Configuration Tool - AUR" --menu "Current: $AUR" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "Install Paru" "Install paru AUR helper" \
        "Install Yay" "Install yay AUR helper" \
        "Config" "Configure AUR helper" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            "Install Paru")
                if [ "$INTERACTIVE" = True ]; then
                    whiptail --msgbox "To install paru:\n\ngit clone https://aur.archlinux.org/paru.git\ncd paru\nmakepkg -si" 20 60 1
                fi
                ;;
            "Install Yay")
                if [ "$INTERACTIVE" = True ]; then
                    whiptail --msgbox "To install yay:\n\ngit clone https://aur.archlinux.org/yay.git\ncd yay\nmakepkg -si" 20 60 1
                fi
                ;;
            "Config")
                if [ "$AUR" = "paru" ]; then
                    EDITOR=${EDITOR:-nano}
                    $EDITOR /etc/paru.conf 2>/dev/null || whiptail --msgbox "Edit ~/.config/paru/paru.conf" 20 60 1
                else
                    whiptail --msgbox "Install an AUR helper first" 20 60 1
                fi
                ;;
        esac
    fi
}

do_update_keyring() {
    if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "Updating keyring...\n\nThis will update archlinux-keyring and parabolic-keyring" 20 60 1
    fi
    pacman -Sy --noconfirm archlinux-keyring parabolic-keyring 2>/dev/null
    if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "Keyring updated" 20 60 1
    fi
}

do_clean_cache() {
    CACHE_SIZE=$(du -sh /var/cache/pacman/pkg 2>/dev/null | cut -f1)
    
    if [ "$INTERACTIVE" = True ]; then
        whiptail --yesno "Clean pacman cache?\nCurrent size: $CACHE_SIZE\n\nThis will remove all cached packages." --defaultno 20 60 2
        RET=$?
    else
        RET=0
    fi
    
    if [ $RET -eq 0 ]; then
        pacman -Scc --noconfirm 2>/dev/null
        whiptail --msgbox "Cache cleaned" 20 60 1
    fi
}

do_sync_packages() {
    if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "Syncing package databases..." 20 60 1
    fi
    pacman -Sy 2>/dev/null
    if [ "$INTERACTIVE" = True ]; then
        whiptail --msgbox "Package databases synced" 20 60 1
    fi
}

do_parch_locale_menu() {
    FUN=$(whiptail --title "Parch Configuration Tool (parch-config)" --menu "Locale & Time" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "L1 Timezone" "Set timezone (timedatectl)" \
        "L2 Locale" "Set system locale" \
        "L3 Keyboard" "Set keyboard layout" \
        "L4 Time Sync" "Enable NTP time sync" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            L1\ *) do_set_timezone ;;
            L2\ *) do_set_locale ;;
            L3\ *) do_set_keyboard ;;
            L4\ *) do_ntp_sync ;;
            *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
        esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
    fi
}

do_set_timezone() {
    CURRENT_TZ=$(timedatectl show --property=Timezone --value 2>/dev/null)
    if [ -z "$CURRENT_TZ" ]; then
        CURRENT_TZ="UTC"
    fi
    
    TIMEZONES="America/New_York America/Los_Angeles America/Chicago Europe/London Europe/Berlin Asia/Tokyo Asia/Tehran UTC"
    
    if [ "$INTERACTIVE" = True ]; then
        TZ=$(whiptail --title "Timezone" --menu "Current: $CURRENT_TZ" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select $TIMEZONES 3>&1 1>&2 2>&3)
        RET=$?
    else
        TZ="$1"
        RET=0
    fi
    
    if [ $RET -eq 0 ] && [ -n "$TZ" ]; then
        timedatectl set-timezone "$TZ"
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "Timezone set to $TZ" 20 60 1
        fi
    fi
}

do_set_locale() {
    LOCALES="en_US.UTF-8 en_GB.UTF-8 de_DE.UTF-8 fr_FR.UTF-8 ja_JP.UTF-8 zh_CN.UTF-8"
    
    if [ "$INTERACTIVE" = True ]; then
        LOC=$(whiptail --title "Locale" --menu "Select locale" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select $LOCALES 3>&1 1>&2 2>&3)
        RET=$?
    else
        LOC="$1"
        RET=0
    fi
    
    if [ $RET -eq 0 ] && [ -n "$LOC" ]; then
        if [ -f /etc/locale.gen ]; then
            sed -i "s/^#*\($LOC.*\)/\1/" /etc/locale.gen
            locale-gen 2>/dev/null
        fi
        localectl set-locale "LANG=$LOC"
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "Locale set to $LOC" 20 60 1
        fi
    fi
}

do_set_keyboard() {
    KEYMAPS="us uk de fr jp"
    
    if [ "$INTERACTIVE" = True ]; then
        KM=$(whiptail --title "Keyboard" --menu "Select keymap" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select $KEYMAPS 3>&1 1>&2 2>&3)
        RET=$?
    else
        KM="$1"
        RET=0
    fi
    
    if [ $RET -eq 0 ] && [ -n "$KM" ]; then
        localectl set-keymap "$KM"
        if command -v setxkbmap >/dev/null 2>&1; then
            setxkbmap "$KM" 2>/dev/null
        fi
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "Keyboard set to $KM" 20 60 1
        fi
    fi
}

do_ntp_sync() {
    if [ "$INTERACTIVE" = True ]; then
        whiptail --yesno "Enable NTP time synchronization?" --defaultno 20 60 2
        RET=$?
    else
        RET=0
    fi
    
    if [ $RET -eq 0 ]; then
        timedatectl set-ntp true
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "NTP enabled" 20 60 1
        fi
    else
        timedatectl set-ntp false
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "NTP disabled" 20 60 1
        fi
    fi
}

check_nvidia_installed() {
    if lsmod | grep -q nvidia 2>/dev/null; then
        echo "yes"
    elif [ -f /usr/bin/nvidia-smi ]; then
        echo "yes"
    elif pacman -Qi nvidia 2>/dev/null | grep -q "nvidia"; then
        echo "yes"
    else
        echo "no"
    fi
}

do_boot_to_desktop() {
    CURRENT_TARGET=$(systemctl get-default)
    
    if [ "$INTERACTIVE" = True ]; then
        whiptail --yesno "Boot to graphical desktop?\nCurrent: $CURRENT_TARGET" --defaultno 20 60 2
        RET=$?
    else
        RET=$1
    fi
    
    if [ $RET -eq 0 ]; then
        systemctl set-default graphical.target
        ASK_TO_REBOOT=1
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "Will boot to graphical desktop" 20 60 1
        fi
    else
        systemctl set-default multi-user.target
        ASK_TO_REBOOT=1
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "Will boot to command line" 20 60 1
        fi
    fi
}

do_plymouth_splash() {
    if ! is_installed plymouth; then
        if [ "$INTERACTIVE" = True ]; then
            whiptail --yesno "Plymouth not installed.\nInstall now?" --defaultno 20 60 2
            RET=$?
        else
            RET=1
        fi
        if [ $RET -eq 0 ]; then
            pacman -S --noconfirm plymouth 2>/dev/null
        fi
    fi
    
    if ! is_installed plymouth; then
        whiptail --msgbox "Plymouth not available" 20 60 1
        return 1
    fi
    
    PLYMOUTH_THEMES=$(plymouth-set-default-theme --list 2>/dev/null | grep -v "^$" | head -10)
    CURRENT_THEME=$(plymouth-set-default-theme 2>/dev/null)
    
    if [ -z "$PLYMOUTH_THEMES" ]; then
        PLYMOUTH_THEMES="bgrt fade-in glowy-logo solar spinfinity"
    fi
    
    FUN=$(whiptail --title "Parch Configuration Tool - Plymouth Splash" --menu "Current: $CURRENT_THEME" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
        "Select Theme" "Choose Plymouth theme" \
        "Enable" "Enable Plymouth splash" \
        "Disable" "Disable Plymouth splash" \
        3>&1 1>&2 2>&3)
    RET=$?
    if [ $RET -eq 1 ]; then
        return 0
    elif [ $RET -eq 0 ]; then
        case "$FUN" in
            "Select Theme")
                if [ "$INTERACTIVE" = True ]; then
                    THEME=$(whiptail --title "Select Theme" --menu "Choose theme" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select $PLYMOUTH_THEMES 3>&1 1>&2 2>&3)
                    RET2=$?
                else
                    THEME="$1"
                    RET2=0
                fi
                if [ $RET2 -eq 0 ] && [ -n "$THEME" ]; then
                    plymouth-set-default-theme "$THEME"
                    if [ "$INTERACTIVE" = True ]; then
                        whiptail --msgbox "Theme set to $THEME" 20 60 1
                    fi
                fi
                ;;
            "Enable")
                if [ -f /boot/loader/entries/parch.conf ]; then
                    sed -i 's/quiet/quiet splash/' /boot/loader/entries/parch.conf 2>/dev/null
                elif [ -f /boot/grub/grub.cfg ]; then
                    sed -i 's/quiet/quiet splash/' /boot/grub/grub.cfg 2>/dev/null
                fi
                if [ "$INTERACTIVE" = True ]; then
                    whiptail --msgbox "Plymouth splash enabled" 20 60 1
                fi
                ;;
            "Disable")
                if [ -f /boot/loader/entries/parch.conf ]; then
                    sed -i 's/splash//' /boot/loader/entries/parch.conf 2>/dev/null
                elif [ -f /boot/grub/grub.cfg ]; then
                    sed -i 's/splash//' /boot/grub/grub.cfg 2>/dev/null
                fi
                if [ "$INTERACTIVE" = True ]; then
                    whiptail --msgbox "Plymouth splash disabled" 20 60 1
                fi
                ;;
        esac
    fi
}

do_autologin() {
    DM=$(get_display_manager)
    
    if [ "$DM" = "none" ]; then
        whiptail --msgbox "No display manager active.\nEnable a display manager first." 20 60 1
        return 1
    fi
    
    CURRENT_USER=$(grep "^User=" /etc/sddm.conf 2>/dev/null | cut -d= -f2)
    [ -z "$CURRENT_USER" ] && CURRENT_USER=$(grep "^autologin-user=" /etc/lightdm/lightdm.conf 2>/dev/null | cut -d= -f2)
    [ -z "$CURRENT_USER" ] && CURRENT_USER="none"
    
    if [ "$INTERACTIVE" = True ]; then
        whiptail --yesno "Enable automatic login?\nCurrent user: $CURRENT_USER" --defaultno 20 60 2
        RET=$?
    else
        RET=$1
    fi
    
    if [ $RET -eq 0 ]; then
        if [ "$INTERACTIVE" = True ]; then
            NEW_USER=$(whiptail --inputbox "Enter username:" 20 60 "$USER" 3>&1 1>&2 2>&3)
            RET2=$?
        else
            NEW_USER="$USER"
            RET2=0
        fi
        
        if [ $RET2 -eq 0 ] && [ -n "$NEW_USER" ]; then
            case "$DM" in
                sddm)
                    [ -f /etc/sddm.conf ] || echo "[Autologin]" > /etc/sddm.conf
                    grep -q "^User=" /etc/sddm.conf && sed -i "s/^User=.*/User=$NEW_USER/" /etc/sddm.conf || echo "User=$NEW_USER" >> /etc/sddm.conf
                    grep -q "^Session=" /etc/sddm.conf || echo "Session=plasma.desktop" >> /etc/sddm.conf
                    ;;
                lightdm)
                    [ -f /etc/lightdm/lightdm.conf ] || echo "[LightDM]" > /etc/lightdm/lightdm.conf
                    sed -i "s/^autologin-user=.*/autologin-user=$NEW_USER/" /etc/lightdm/lightdm.conf 2>/dev/null || echo "autologin-user=$NEW_USER" >> /etc/lightdm/lightdm.conf
                    ;;
                gdm)
                    mkdir -p /etc/gdm/custom.conf 2>/dev/null
                    sed -i "s/^AutomaticLogin=.*/AutomaticLogin=$NEW_USER/" /etc/gdm/custom.conf 2>/dev/null || echo "AutomaticLogin=$NEW_USER" >> /etc/gdm/custom.conf
                    ;;
            esac
            ASK_TO_REBOOT=1
            if [ "$INTERACTIVE" = True ]; then
                whiptail --msgbox "Auto login enabled for $NEW_USER" 20 60 1
            fi
        fi
    elif [ $RET -eq 1 ]; then
        case "$DM" in
            sddm)
                sed -i "/^User=/d" /etc/sddm.conf 2>/dev/null
                ;;
            lightdm)
                sed -i "/^autologin-user=/d" /etc/lightdm/lightdm.conf 2>/dev/null
                ;;
            gdm)
                sed -i "/^AutomaticLogin=/d" /etc/gdm/custom.conf 2>/dev/null
                ;;
        esac
        ASK_TO_REBOOT=1
        if [ "$INTERACTIVE" = True ]; then
            whiptail --msgbox "Auto login disabled" 20 60 1
        fi
    fi
}

#
# Interactive use loop
#
# Interactive use loop
#
if [ "$INTERACTIVE" = True ]; then
  [ -e $CONFIG ] || touch $CONFIG
  calc_wt_size
  while [ "$USER" = "root" ] || [ -z "$USER" ]; do
    if ! USER=$(whiptail --inputbox "parch-config could not determine the default user.\n\nWhat user should these settings apply to?" 20 60 "$USER" 3>&1 1>&2 2>&3); then
      return 0
    fi
  done
  while true; do
    if is_rpi ; then
      MEMSIZE=$(vcgencmd get_config total_mem|cut -d= -f2)
      if [ $MEMSIZE -lt 1024 ]; then
        FMEMSIZE="${MEMSIZE}MB"
      else
        FMEMSIZE="$(expr $MEMSIZE / 1024)GB"
      fi
      FUN=$(whiptail --title "Parch Configuration Tool (parch-config)" --backtitle "$(cat /proc/device-tree/model 2>/dev/null || echo 'Parch Linux'), ${FMEMSIZE}" --menu "Setup Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Finish --ok-button Select \
        "1 System Options" "Configure system settings" \
        "2 Display Options" "Configure display settings" \
        "3 Interface Options" "Configure connections to peripherals" \
        "4 Performance Options" "Configure performance settings" \
        "5 Localisation Options" "Configure language and regional settings" \
        "6 Advanced Options" "Configure advanced settings" \
        "7 Parch Settings" "Parch-specific configuration" \
        "8 Update" "Update this tool to the latest version" \
        "9 About parch-config" "Information about this configuration tool" \
        3>&1 1>&2 2>&3)
    elif is_x86_64 || is_wsl; then
      DM=$(get_display_manager)
      DE=$(get_desktop_environment)
      FUN=$(whiptail --title "Parch Configuration Tool (parch-config)" --backtitle "Platform: $PLATFORM | DM: $DM | DE: $DE" --menu "Setup Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Finish --ok-button Select \
        "1 System Options" "Configure system settings" \
        "2 Display Options" "Configure display settings" \
        "3 Interface Options" "Configure connections to peripherals" \
        "4 Desktop Settings" "Display manager and desktop config" \
        "5 Localisation Options" "Configure language and regional settings" \
        "6 Parch Settings" "Parch-specific configuration" \
        "7 Services" "System services (SSH, Bluetooth, etc.)" \
        "8 Update" "Update this tool to the latest version" \
        "9 About parch-config" "Information about this configuration tool" \
        3>&1 1>&2 2>&3)
    else
      FUN=$(whiptail --title "Parch Configuration Tool (parch-config)" --backtitle "Platform: $PLATFORM" --menu "Setup Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Finish --ok-button Select \
        "1 System Options" "Configure system settings" \
        "2 Display Options" "Configure display settings" \
        "3 Interface Options" "Configure connections to peripherals" \
        "5 Localisation Options" "Configure language and regional settings" \
        "6 Advanced Options" "Configure advanced settings" \
        "7 Parch Settings" "Parch-specific configuration" \
        "8 Update" "Update this tool to the latest version" \
        "9 About parch-config" "Information about this configuration tool" \
        3>&1 1>&2 2>&3)
    fi
    RET=$?
    if [ $RET -eq 1 ]; then
      do_finish
    elif [ $RET -eq 0 ]; then
      case "$FUN" in
        1\ *) do_system_menu ;;
        2\ *) do_display_menu ;;
        3\ *) do_interface_menu ;;
        4\ *) if is_x86_64 || is_wsl; then do_parch_desktop_menu; else do_performance_menu; fi ;;
        5\ *) if is_x86_64 || is_wsl; then do_parch_locale_menu; else do_internationalisation_menu; fi ;;
        6\ *) if is_x86_64 || is_wsl; then do_parch_package_menu; else do_advanced_menu; fi ;;
        7\ *) if is_x86_64 || is_wsl; then do_parch_services_menu; else do_advanced_menu; fi ;;
        8\ *) do_update ;;
        9\ *) do_about ;;
        *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
      esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
    else
      exit 1
    fi
  done
  while true; do
    if is_pi ; then
      MEMSIZE=$(vcgencmd get_config total_mem|cut -d= -f2)
      if [ $MEMSIZE -lt 1024 ]; then
        FMEMSIZE="${MEMSIZE}MB"
      else
        FMEMSIZE="$(expr $MEMSIZE / 1024)GB"
      fi
      FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --backtitle "$(cat /proc/device-tree/model), ${FMEMSIZE}" --menu "Setup Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Finish --ok-button Select \
        "1 System Options" "Configure system settings" \
        "2 Display Options" "Configure display settings" \
        "3 Interface Options" "Configure connections to peripherals" \
        "4 Performance Options" "Configure performance settings" \
        "5 Localisation Options" "Configure language and regional settings" \
        "6 Advanced Options" "Configure advanced settings" \
        "8 Update" "Update this tool to the latest version" \
        "9 About parch-config" "Information about this configuration tool" \
        3>&1 1>&2 2>&3)
    else
      FUN=$(whiptail --title "Raspberry Pi Software Configuration Tool (parch-config)" --menu "Setup Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Finish --ok-button Select \
        "1 System Options" "Configure system settings" \
        "2 Display Options" "Configure display settings" \
        "3 Interface Options" "Configure connections to peripherals" \
        "5 Localisation Options" "Configure language and regional settings" \
        "6 Advanced Options" "Configure advanced settings" \
        "8 Update" "Update this tool to the latest version" \
        "9 About parch-config" "Information about this configuration tool" \
        3>&1 1>&2 2>&3)
    fi
    RET=$?
    if [ $RET -eq 1 ]; then
      do_finish
    elif [ $RET -eq 0 ]; then
      case "$FUN" in
        1\ *) do_system_menu ;;
        2\ *) do_display_menu ;;
        3\ *) do_interface_menu ;;
        4\ *) do_performance_menu ;;
        5\ *) do_internationalisation_menu ;;
        6\ *) do_advanced_menu ;;
        8\ *) do_update ;;
        9\ *) do_about ;;
        *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
      esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
    else
      exit 1
    fi
  done
fi
