#!/bin/bash

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

# (c) 11/2004, 09/2006 by Erik Auerswald <auerswal@unix-ag.uni-kl.de>

# Version 1.4

### variables ###

declare GAMEPORTS		# chosen gameport hardware
declare -i PC_GAMEPORT		# any PC gameport chosen?
declare -i SERIAL		# serial port chosen?
declare JOYSTICKS		# chosen joystick hardware
declare INPUTATTACH		# commandline to attach serial joysticks
declare ANALOG_OPTIONS		# options for module analog
declare MODULES			# modules to be loaded to activate config
declare DEBUG			# control debug output:
				#  0 - no debug output
				#  1 - some debug output
				#  2 - more debug output
				#  (2 means all debug output right now)
declare DRYRUN			# don't (un)load modules and inputattch if set
declare PATH_TO_LSJOYSTICK	# non standard location on lsjoystick
				# i.e. not in /sbin:/usr/sbin:/bin:/usr/bin
declare SUDO=""			# to use sudo
declare helptext_main="Chose one of the actions presented in the menu:\n\n\
\"show detected joysticks\" displays a list of the joysticks currently\
 detected.\n\n\
\"configure joystick hardware\" lets you configure what type(s) of\
 joystick(s)\n are connected to what kind(s) of gameport(s). Use \"show\
 detected joysticks\"\nto check the configuration.\n\n\
You can re-configure as often as you want.\n\n\
When you are satisfied with the configuration, press the Quit button."

declare analog_options_help="\
Specify the type of analog joystick connected to the gameport.

Autodetection will only work for one connected analog joystick with up to
4 buttons and 4 axes or a Saitek Cyborg joystick.

Use \"custom\" to manually specify the set of features of your joystick."

declare analog_custom_help="\
Select the features of your joystick. You can specify any combination,
but not all make sense. Check the result with \"show detected joysticks\"
from the main menu."

declare inputattach_help="\
Select which type of joystick is connected to this serial port."

declare joystick_type_help="\
Select \"PC style joystick\" for a \"standard\" joystick. Up to 4 independent
axes and 4 independent buttons can be used. Some kinds of joysticks map
additional buttons or so called \"hats\" to an axis.

The other variants are digital joysticks that use some custom protokoll to
communicate over the game port. This is usually used to have more than 4
independent axes and 4 independent buttons per gameport. See the tooltips
to get more information about which models are supported."

declare used_serial_ports_help="\
Select any serial port to which a joystick is connected. You will
be asked what joystick is connected to these ports afterwards."

declare gameports_help="\
Select the connection type(s) used for your joystick(s).

USB joysticks don't need any configuration.

Joysticks connected to a serial port must be configured by selecting the type
of joystick.

The other options specify some kind of gameport hardware (with a 15pin
connector). The type of joystick connected to any of these gameports
has to be configured."

# initialize DEBUG variable
if [ -z "${DEBUG}" ]
then
  DEBUG=0
fi

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: checking for superuser rights"
fi
# checking for superuser rights
if [ ${EUID} -ne 0 ]
then
  if [ ${DEBUG} -ge 1 ]
  then
    echo "DEBUG: EUID=${EUID}, using sudo"
  fi
  SUDO=sudo
fi

### functions ###

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of configure_analog"
fi

# display a radiolist to chose from predefined joysticks
# or chose custom setup and display a checklist of supported features
function configure_analog()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin configure_analog"
  fi
  local new_analog_options
  local sum
  local i
  if [ -z "${1}" ]
  then
    echo "Error: configure_analog() used without parameter" > /dev/stderr
    return
  fi
  new_analog_options=\
$(Xdialog --title "$(basename $0)" --stdout --separate-output --item-help \
 --no-tags --help "${analog_options_help}" --radiolist \
  "What kind of PC style joystick is connected to ${1}?" 0 0 0 \
  "none" "none" "off" "no analog joystick on ${1}" \
  "auto" "autodetect" "on" "autodetect joystick (joysticks with up to 4 buttons and 4 axes or Saitek Cyborg)" \
  "2btn" "joystick with 2 buttons" "off" "joystick with 2 buttons and up to 4 axes" \
  "y-joy" "two joysticks, each w. 2 buttons and 2 axes" "off" "two joysticks, each with 2 buttons and 2 axes, connected via an Y-cable or 2 ports on one gamecard" \
  "y-pad" "two gamepads, each w. 2 buttons and 2 axes" "off" "two gamepads, each with 2 buttons and 2 axes, connected via an Y-cable or 2 ports on one gamecard" \
  "fcs" "Thrustmaster FCS" "off" "Thrustmaster Flight Control System or compatible joystick" \
  "chf" "simple CH Flightstick" "off" "CH Flightstick or compatible joystick (this is a joystick with hat)" \
  "fullchf" "CH Flightstick" "off" "CH Flightstick or compatible joystick with 2 hats and 6 buttons" \
  "gamepad" "4 or 6 button gamepad" "off" "gamepad with 4 or 6 buttons" \
  "gamepad8" "8 button, 2 axis gamepad" "off" "gamepad with 8 buttons and 2 axes" \
  "custom" "custom joystick" "off" "define the features of your joystick" )
  # if custom was chosen, display a checklist with existing features
  if [ "${new_analog_options}a" = "customa" ]
  then
    new_analog_options=$( \
    Xdialog --title "$(basename $0)" --stdout --separate-output --item-help \
    --no-tags --help "${analog_custom_help}" --checklist \
    "What features has the joystick on ${1}?" 0 0 0\
    "1" "Axis X1" "off" "Axis X1" \
    "2" "Axis Y1" "off" "Axis Y1" \
    "4" "Axis X2" "off" "Axis X2" \
    "8" "Axis Y2" "off" "Axis Y2" \
    "16" "Button A" "off" "Button A" \
    "32" "Button B" "off" "Button B" \
    "64" "Button C" "off" "Button C" \
    "128" "Button D" "off" "Button D" \
    "256" "CHF Buttons X and Y" "off" "CHF Buttons X and Y" \
    "512" "CHF Hat 1" "off" "CHF Hat 1" \
    "1024" "CHF Hat 2" "off" "CHF Hat 2" \
    "2048" "FCS Hat" "off" "FCS Hat" \
    "4096" "Pad Button X" "off" "Pad Button X" \
    "8192" "Pad Button Y" "off" "Pad Button Y" \
    "16384" "Pad Button U" "off" "Pad Button U" \
    "32768" "Pad Button V" "off" "Pad Button V" \
    "65536" "Saitek F1-F4 Buttons" "off" "Saitek F1-F4 Buttons" \
    "131072" "Saitek Digital Mode" "off" "Saitek Digital Mode" \
    "524288" "GamePad" "off" "GamePad" \
    "1048576" "Joy2 Axis X1" "off" "Joy2 Axis X1" \
    "2097152" "Joy2 Axis Y1" "off" "Joy2 Axis Y1" \
    "4194304" "Joy2 Axis X2" "off" "Joy2 Axis X2" \
    "8388608" "Joy2 Axis Y2" "off" "Joy2 Axis Y2" \
    "16777216" "Joy2 Button A" "off" "Joy2 Button A" \
    "33554432" "Joy2 Button B" "off" "Joy2 Button B" \
    "67108864" "Joy2 Button C" "off" "Joy2 Button C" \
    "134217728" "Joy2 Button D" "off" "Joy2 Button D" \
    "2147483648" "Joy2 GamePad" "off" "Joy2 GamePad" \
    )
    # DEBUG output
    if [ ${DEBUG} -ge 1 ]
    then
      echo "DEBUG: new_analog_options=${new_analog_options}"
    fi
    # combine numbers in new_analog_options
    sum=0
    for i in ${new_analog_options}
    do
      let sum+=$i
    done
    # DEBUG output
    if [ ${DEBUG} -ge 1 ]
    then
      echo "DEBUG: sum=${sum}"
    fi
    new_analog_options=${sum}
    # DEBUG output
    if [ ${DEBUG} -ge 1 ]
    then
      echo "DEBUG: new_analog_options=${new_analog_options}"
    fi
  fi
  # build option string
  if [ -n "${new_analog_options}" ]
  then
    ANALOG_OPTIONS="${ANALOG_OPTIONS},${new_analog_options}"
  fi
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end configure_analog"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of configure_analog"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of configure_serial"
fi

# display a radiolist to chose joystick connected to serial port
# serial port needs to be given as first parameter
function configure_serial()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin configure_serial"
  fi
  local new_inputattach
  if [ -z "${1}" ]
  then
    echo "Error: configure_serial() used without parameter" > /dev/stderr
    return
  fi
  new_inputattach=$(
  Xdialog --title "$(basename $0)" --stdout --separate-output --item-help \
  --no-tags --help "${inputattach_help}" --radiolist \
  "What kind of joystick is connected to serial port ${1#*S} (device ${1})?" 0 0 0 \
  "spaceorb" "SpaceOrb" "off" "SpaceTec SpaceBall Avenger or SpaceOrb 360" \
  "spaceball" "SpaceBall" "off" "SpaceTec SpaceBall 2003/3003/4000 FLX" \
  "magellan" "Magellan / Space Mouse" "off" "Magellan 3D or Space Mouse" \
  "warrior" "Logitech SWIFT" "off" "Logitech WingMan Warrior" \
  "iforce" "I-Force" "off" "I-Force joysticks and wheels driver (e.g. AVB Mag Turbo Force, Top Shot Pegasus, Logitech WingMan Force, WingMan Force 3D, WingMan Force Wheel, WingMan Strike Force 3D or Guillemot Race Leader Force Feedback)" \
  "stinger" "Gravis Stinger" "off" "Gravis Stinger gamepad" \
  "twiddler-joy" "Handykey Twiddler" "off" "Handykey Twiddler used as a joystick" \
  "none" "no joystick" "on" "to chose none of the above" )
  case ${new_inputattach} in
    spaceorb)		INPUTATTACH="${INPUTATTACH} spaceorb_${1}";;
    spaceball)		INPUTATTACH="${INPUTATTACH} spaceball_${1}";;
    warrior)		INPUTATTACH="${INPUTATTACH} warrior_${1}";;
    magellan)		INPUTATTACH="${INPUTATTACH} magellan_${1}";;
    iforce)		INPUTATTACH="${INPUTATTACH} iforce_${1}";;
    stinger)		INPUTATTACH="${INPUTATTACH} stinger_${1}";;
    twiddler-joy)	INPUTATTACH="${INPUTATTACH} twiddler-joy_${1}";;
  esac
  # sanitize INPUTATTACH
  INPUTATTACH=$(echo ${INPUTATTACH} | sed 's/[[:space:]]*;[[:space:]]*$//')
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end configure_serial"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of configure_serial"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of configure_pc_joystick"
fi

# display a radiolist to chose the connected joystick(s)
# $1 needs to contain the name of the gameport hardware
function chose_pc_joystick()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin chose_pc_joystick"
  fi
  local new_joysticks
  if [ -z "${1}" ]
  then
    echo "Error: chose_pc_joystick() used without parameter" > /dev/stderr
    return
  fi
  # display dialogbox to chose which joysticks are connected to gameport
  new_joysticks=$(
  Xdialog --title "$(basename $0)" --stdout --separate-output --item-help \
  --no-tags --help "${joystick_type_help}" --radiolist \
  "What kind of joystick is connected to ${1}?" 0 0 0 \
  "analog" "PC style joystick" "off" "standard 'analog' PC joystick or gamepad or Saitek Cyborg" \
  "sidewinder" "Microsoft Sidewinder" "off" "Microsoft SideWinder 3D Pro, Force Feedback Pro, Force Feedback Wheel, FreeStyle Pro, GamePad, Precision Pro or Precision Pro USB" \
  "adi" "Logitech ADI" "off" "Logitech CyberMan 2, ThunderPad Digital, WingMan Extreme Digital, WingMan Formula, WingMan Interceptor, WingMan GamePad, WingMan GamePad USB, WingMan GamePad Extreme or WingMan Extreme Digital 3D" \
  "grip" "Gravis GrIP" "off" "Gravis GamePad Pro, Gravis BlackHawk Digital, Gravis Xterminator or Gravis Xterminator DualControl" \
  "grip_mp" "Gravis GrIP Multiport" "off" "Gravis GrIP Multiport" \
  "a3d" "A3D" "off" "FPGaming Assassin 3D, MadCatz Panther or Panther XL" \
  "tmdc" "ThrustMaster DirectConnect" "off" "ThrustMaster Millenium 3D Inceptor, 3D Rage Pad, Fusion Digital Game Pad, FragMaster or Attack Throttle" \
  "cobra" "Creative Labs Blaster" "off" "Creative Blaster GamePad Cobra" \
  "gf2k" "Genius Digital" "off" "Genius Flight2000 F-23 joystick, Flight2000 F-31 joystick or G-09D gamepad" \
  "interact" "InterAct Digital" "off" "InterAct HammerHead/FX or ProPad8" \
  "guillemot" "Guillemot Digital" "off" "Guillemot Digital joystick" \
  "none" "no joystick" "on" "to chose none of the above" )
  # if analog joystick chosen, configure it
  # otherwise, add option none to ANALOG_OPTIONS
  if [ "${new_joysticks}a" = "analoga" ]
  then
    configure_analog ${1}
  else
    ANALOG_OPTIONS="${ANALOG_OPTIONS},none"
  fi
  if [ -n "${new_joysticks}" ]
  then
    JOYSTICKS="${JOYSTICKS} ${new_joysticks}"
  fi
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end chose_pc_joystick"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of configure_pc_joystick"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of chose_serial"
fi

# display a radiolist to chose the connected joystick(s)
# chose the serial port(s) to which joysticks are connected
function chose_serial()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin chose_serial"
  fi
  local serial_ports_list
  local used_serial_ports
  local port
  # I don't have any serial joysticks so this is not well tested
  echo "Warning: serial joystick support is untested" > /dev/stderr
  # display dialogbox to chose serial devices
  # find serial ports via `ls /dev/ttyS*`, transform this into a parameter list
  # for Xdialog
  serial_ports_list=$(echo /dev/ttyS* | sed 's/\([a-zA-Z/]\+\)\([0-9]\+\) */\1\2\tSerial Port No \2\toff\tdevice \1\2\n/g' | sort -nk 5,5 | paste -s)
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: serial_ports_list=${serial_ports_list}"
  fi
  # display a checklist to chose the serial ports
  # use internal field separator to separate parameters containing spaces
  IFS=$(echo -e "\t\n")
  used_serial_ports=$(
  Xdialog --title "$(basename $0)" --stdout --separate-output --item-help \
  --no-tags --help "${used_serial_ports_help}" --checklist \
  "To which serial ports are joysticks connected?" 0 0 0 \
  $(echo "${serial_ports_list[@]}"))
  unset IFS
  # configure joysticks connected to chosen serial ports
  for port in ${used_serial_ports}
  do
    configure_serial ${port}
  done
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end chose_serial"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of chose_serial"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of chose_gameports"
fi

# display a radiolist to chose the connected joystick(s)
# get a list of gameports
# joysticks connected (via adapters) to the parallel port are not yet supported
# by this config program
function chose_gameports()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin chose_gameports"
  fi
  local new_gameports
  new_gameports=$(
  Xdialog --title "$(basename $0)" --stdout --separate-output --item-help \
  --no-tags --help "${gameports_help}" --checklist \
  "Connection type(s) of joystick(s)?" 0 0 0 \
  "usb" "USB" "on" "joystick(s) connected via USB" \
  "ns558" "PC gameport" "off" "joystick(s) connected to a PC gameport" \
  "lightning" "Ligtning 4 gamecard" "off" \
    "joystick(s) connected to a PDPI Lightning 4 gamecard" \
  "vortex" "Vortex soundcard" "off" \
    "joystick(s) connected to an Aureal Vortex or Vortex2 soundcard" \
  "cs461x" "SoundFusion soundcard" "off" "joystick(s) connected to a Crystal SoundFusion soundcard" \
  "emu10k1-gp" 'SB Live! soundcard' "off" 'joystick(s) connected to a SoundBlaster Live! soundcard' \
  "fm801-gp" 'FM 801 soundcard' "off" 'joystick(s) connected to an FM 801 soundcard' \
  "soundcard" "SB 64/128, ES1370/ES1371, Solo1, SonicVibes soundcard" "off" \
    "joystick(s) connected to a SoundBlaster 64 and 128 - ES1370 and ES1371, ESS Solo1 and S3 SonicVibes PCI soundcard" \
  "serport" "serial port" "off" "joystick(s) connected to serial port(s)")
  # keep old config on cancel button
  if [ $? -eq 0 ]
  then
    GAMEPORTS=${new_gameports}
  fi
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end chose_gameports"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of chose_gameports"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of unload_all_modules"
fi

# display a radiolist to chose the connected joystick(s)
# function to unload gameport and joystick modules
function unload_all_modules()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin unload_all_modules"
  fi
  # all modules to unload
  # order is important as some modules depend on others
  local all_modules="analog sidewinder adi grip a3d tmdc cobra gf2k interact \
                     spaceorb spaceball warrior magellan iforce stinger \
		     grip_mp guillemot \
		     ns558 lightning vortex cs461x emu10k1-gp fm801-gp \
		     gameport joydev serport"
  # kill all inputattach processes
  # DEBUG output
  if [ ${DEBUG} -ge 1 ]
  then
    echo "DEBUG: killing inputattach processes"
  fi
  if [ -z "${DRYRUN}" ]
  then
    ${SUDO} killall -q inputattach
  else
    echo "DRYRUN: ${SUDO} killall -q inputattach"
  fi
  # now unload all modules
  # DEBUG output
  if [ ${DEBUG} -ge 1 ]
  then
    echo "DEBUG: unloading all gameport / joystick modules"
  fi
  if [ ${DEBUG} -ge 1 ]
  then
    echo "DEBUG: all_modules=${all_modules}"
  fi
  if [ -z "${DRYRUN}" ]
  then
    ${SUDO} modprobe -qr ${all_modules}
  else
    echo "DRYRUN: ${SUDO} modprobe -qr ${all_modules}"
  fi
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end unload_all_modules"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of unload_all_modules"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of unload_modules"
fi

# display a radiolist to chose the connected joystick(s)
# function to unload modules
function unload_modules
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin unload_modules"
  fi
  local reversed_modules
  local module
  # unload modules to load gameport drivers in correct order later
  # only those modules are unloaded that are configured by this program
  # if there are some gameport/joystick modules loaded by other means or
  # an old run of this program, they stay untouched, so you can configure
  # gameport/joystick hardware independent of this tool
  # modules need to be unloaded in the correct order
  # as gameport is added in front of game port hardware, we can just reverse
  # the order of $MODULES
  for module in ${MODULES}
  do
    reversed_modules="${module} ${reversed_modules}"
  done
  for module in ${reversed_modules}
  do
    # DEBUG output
    if [ ${DEBUG} -ge 1 ]
    then
      echo "DEBUG: module=${module}"
    fi
    # check, if module already loaded
    if [ -n "$(lsmod | grep ${module//-/_})" ]
    then
      # DEBUG output
      if [ ${DEBUG} -ge 1 ]
      then
        echo "DEBUG: module ${module} already loaded, trying to unload it"
      fi
      # modules under control of this program should be reloaded
      # exceptions: input hid serial
      case ${module} in
        input | hid | serial)
          # don't touch these modules
          if [ ${DEBUG} -ge 1 ]
          then
            echo "DEBUG: module ${module} will not be unloaded"
          fi;;
        *)
          # DEBUG output
  	if [ ${DEBUG} -ge 1 ]
  	then
  	  echo "DEBUG: unloading module ${module}"
  	fi
	if [ -z "${DRYRUN}" ]
	then
	  ${SUDO} modprobe -qr ${module}
	else
	  echo "DRYRUN: ${SUDO} modprobe -qr ${module}"
	fi
      esac
    fi
  done
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end unload_modules"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of unload_modules"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of load_modules"
fi

# display a radiolist to chose the connected joystick(s)
# function to load the configured modules
function load_modules()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin load_modules"
  fi
  # now load the modules
  for module in ${MODULES}
  do
    # DEBUG output
    if [ ${DEBUG} -ge 1 ]
    then
      echo "DEBUG: module=${module}"
    fi
    # check, if module already loaded
    if [ -n "$(lsmod | grep ${module})" ]
    then
      # DEBUG output
      if [ ${DEBUG} -ge 1 ]
      then
        echo "DEBUG: module ${module} already loaded"
      fi
    else
      # load module
      # module analog needs options
      if [ "${module}a" = "analoga" ]
      then
        # DEBUG use echo to show what we want to do
        # DEBUG output
        if [ ${DEBUG} -ge 1 ]
        then
          echo "DEBUG: loading module ${module} map=${ANALOG_OPTIONS}"
        fi
	if [ -z "${DRYRUN}" ]
	then
	  ${SUDO} modprobe -q ${module} map=${ANALOG_OPTIONS}
	else
	  echo "DRYRUN: ${SUDO} modprobe -q ${module} map=${ANALOG_OPTIONS}"
	fi
      else
        # DEBUG use echo to show what we want to do
        # DEBUG output
        if [ ${DEBUG} -ge 1 ]
        then
          echo "DEBUG: loading module ${module}"
        fi
	if [ -z "${DRYRUN}" ]
	then
	  ${SUDO} modprobe -q ${module}
	else
	  echo "DRYRUN: ${SUDO} modprobe -q ${module}"
	fi
      fi
    fi
  done
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end load_modules"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of load_modules"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of create_device_nodes"
fi

# display a radiolist to chose the connected joystick(s)
# function to (re)create joystick devices
# wants number of devices as first parameter, uses 16 as default
function create_device_nodes()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin create_device_nodes"
  fi
  # get number from first parameter or use the default of 16
  local number_of_nodes=${1:-16}
  local node
  if [ -n "$(ps ax|grep [u]devd)" ]
  then
    echo "Error: udev creates device nodes" > /dev/stderr
    Xdialog --title "$(basename $0)" --infobox \
    "Error: udev creates device nodes" 0 0 10000
    return
  fi
  if [ -n "$(grep '[[:space:]]devfs[[:space:]]' /etc/mtab)" ]
  then
    echo "Error: devfs creates device nodes" > /dev/stderr
    Xdialog --title "$(basename $0)" --infobox \
    "Error: devfs creates device nodes" 0 0 10000
    return
  fi
  if [ ${number_of_nodes} -lt 4 ]
  then
    echo "Warning: refusing to create less than 4 joystick devices"\
      > /dev/stderr
    echo "Warning: creating 4 device nodes" > /dev/stderr
    number_of_nodes=4
  fi
  # unload joystick modules to be able to remove device nodes
  unload_all_modules
  # remove existing device nodes
  # DEBUG output
  if [ ${DEBUG} -ge 1 ]
  then
    echo "DEBUG: removing /dev/js*, /dev/input/js* and /dev/input/event*"
  fi
  if [ "$(uname -n)a" = "Knoppixa" ]
  then
    if [ -z "${DRYRUN}" ]
    then
      ${SUDO} rm -f /dev/js*
    else
      echo "DRYRUN: ${SUDO} rm -f /dev/js*"
    fi
  else
    if [ -z "${DRYRUN}" ]
    then
      ${SUDO} rm -f /dev/js* /dev/input/js* /dev/input/event*
    else
      echo "DRYRUN: ${SUDO} rm -f /dev/js* /dev/input/js* /dev/input/event*"
    fi
  fi
  for node in $(seq 0 $((${number_of_nodes}-1)))
  do
    # DEBUG output
    if [ ${DEBUG} -ge 1 ]
    then
      echo "DEBUG: creating device node ${node}"
    fi
    # cannot create all devices on knoppix
    if [ "$(uname -n)a" = "Knoppixa" ]
    then
      if [ -z "${DRYRUN}" ]
      then
	${SUDO} mknod /dev/js${node} c 13 ${node}
      else
	echo "DRYRUN: ${SUDO} mknod /dev/js${node} c 13 ${node}"
      fi
    else
      if [ -z "${DRYRUN}" ]
      then
	${SUDO} mknod /dev/input/js${node} c 13 ${node}
	${SUDO} ln -s input/js${node} /dev/js${node}
	${SUDO} mknod /dev/input/event${node} c 13 $((${node}+64))
      else
	echo "DRYRUN: ${SUDO} mknod /dev/input/js${node} c 13 ${node}"
	echo "DRYRUN: ${SUDO} ln -s input/js${node} /dev/js${node}"
	echo "DRYRUN: ${SUDO} mknod /dev/input/event${node} c 13 $((${node}+64))"
      fi
    fi
  done
  # load configured modules
  load_modules
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end create_device_nodes"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of create_device_nodes"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of add_gameport_modules"
fi

# display a radiolist to chose the connected joystick(s)
# function to add modules for gameport hardware to $MODULES
function add_gameport_modules()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin add_gameport_modules"
  fi
  for gameport in ${GAMEPORTS}
  do
    case ${gameport} in
      # USB might need HID module, further USB configuration is not the purpose
      # of this configuration program
      usb)
        if [ -z "$(echo ${MODULES} | fgrep hid)" ]
	then
          MODULES="${MODULES} hid"
	fi;;
      # use of any gameport compatible hardware needs the gameport module as
      # well as the specific gameport module
      ns558 | lightning | cs461x | emu10k1-gp | fm801-gp | vortex)
        if [ ${PC_GAMEPORT} -eq 0 ]
        then
          MODULES="${MODULES} gameport"
	  PC_GAMEPORT=1
        fi
        if [ -z "$(echo ${MODULES} | fgrep ${gameport})" ]
	then
          MODULES="${MODULES} ${gameport}"
	fi;;
      # this program does not (yet?) configure the sound driver, it assumes a
      # working configuration and configures the joystick(s) connected to the
      # soundcard
      soundcard)
        if [ ${PC_GAMEPORT} -eq 0 ]
        then
          MODULES="${MODULES} gameport"
  	  PC_GAMEPORT=1
        fi;;
      # use of the serial port might need some modules, but which?
      serport)
        if [ ${SERIAL} -eq 0 ]
        then
	  # add needed modules here
          MODULES="serport ${MODULES}"
	  SERIAL=1
        fi;;
    esac
  done
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end add_gameport_modules"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of add_gameport_modules"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of chose_joysticks"
fi

# display a radiolist to chose the connected joystick(s)
# function to chose joystick drivers
function chose_joysticks()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin chose_joysticks"
  fi
  # only pc style gameports and serial port need special drivers
  if [ \( ${PC_GAMEPORT} -eq 1 \) -o \( ${SERIAL} -eq 1 \) ]
  then
    # chose joysticks
    for gameport in ${GAMEPORTS}
    do
      case ${gameport} in
        ns558)		chose_pc_joystick "PC Gameport";;
        lightning)	chose_pc_joystick "Lightning 4 Gamecard";;
        vortex)		chose_pc_joystick "Aureal Vortex or Vortex2";;
        cs461x)		chose_pc_joystick "Crystal SoundFusion";;
        emu10k1-gp)	chose_pc_joystick 'SoundBlaster Live!';;
	fm801-gp)	chose_pc_joystick "FM 801";;
        soundcard)	chose_pc_joystick "Soundcard";;
        serport)	chose_serial;;
      esac
    done
    # sanitize $JOYSTICKS
    JOYSTICKS=$(echo "${JOYSTICKS}" | sed 's/ none//g;s/^ \+//;s/ \+/\n/g' | \
                sort -u)
    JOYSTICKS=$(echo ${JOYSTICKS})
    # append joystick modules to $MODULES
    if [ -n "${JOYSTICKS}" ]
    then
      MODULES="${MODULES} ${JOYSTICKS}"
    fi
  fi
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end chose_joysticks"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of chose_joysticks"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of attach_serial"
fi

# display a radiolist to chose the connected joystick(s)
# function to attach serial joysticks
function attach_serial()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin attach_serial"
  fi
  local input
  # if serial joystick(s) configured invoke inputattach
  if [ ${SERIAL} -eq 1 ]
  then
    # kill all existing inputattach processes
    if [ -z "${DRYRUN}" ]
    then
      ${SUDO} killall -q inputattach
    else
      echo "DRYRUN: ${SUDO} killall -q inputattach"
    fi
    # start configured inputattach processes
    if [ -n "${INPUTATTACH}" ]
    then
      if [ ${DEBUG} -ge 1 ]
      then
	echo "DEBUG: INPUTATTACH=${INPUTATTACH}"
      fi
      for input in ${INPUTATTACH}
      do
	if [ -z "${DRYRUN}" ]
	then
	  ${SUDO} inputattach --${input%%_*} ${input##*_} &
	else
	  echo "DRYRUN: ${SUDO} inputattach --${input%%_*} ${input##*_} &"
	fi
      done
    fi
  fi
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end attach_serial"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of attach_serial"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of show_joystick_list"
fi

# display a radiolist to chose the connected joystick(s)
# function to display list of joysticks known to the system
function show_joystick_list()
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin show_joystick_list"
  fi
  # use lsjoystick to check for joysticks and display the results in a textbox
  lsjoystick -qlg | Xdialog --no-cancel --fixed-font --title \
  "$(basename $0): joysticks found" --textbox - 0 0
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end show_joystick_list"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of show_joystick_list"
fi
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: before definition of configure_hardware"
fi

# display a radiolist to chose the connected joystick(s)
# function to configure driver modules
function configure_hardware
{
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: begin configure_hardware"
  fi
  # chose gameports
  chose_gameports
  # if no gameport is chosen, there is no need to configure anything
  # otherwise, the input subsystem and the module joydev are needed at least
  if [ -n "${GAMEPORTS}" ]
  then
    MODULES="input joydev"
    # the different gameports need their driver modules
    add_gameport_modules
    # configuration of the different joystick types
    # (depending on connection hardware)
    chose_joysticks
    # sanitize $ANALOG_OPTIONS
    ANALOG_OPTIONS=$(echo ${ANALOG_OPTIONS} | sed 's/^,//')
    # now a configuration is chosen
    # the needed modules are stored in $MODULES
    # the options for module analog are stored in $ANALOG_OPTIONS
    # the commandline to invoke inputattach is stored in $INPUTATTACH
    # DEBUG output:
    if [ ${DEBUG} -ge 1 ]
    then
      echo "DEBUG: MODULES=${MODULES}"
    fi
    if [ ${DEBUG} -ge 1 ]
    then
      echo "DEBUG: ANALOG_OPTIONS=${ANALOG_OPTIONS}"
    fi
    if [ ${DEBUG} -ge 1 ]
    then
      echo "DEBUG: INPUTATTACH=${INPUTATTACH}"
    fi
    # unload configured modules to get a consistent state
    unload_modules
    # load configured modules
    load_modules
    # use inputattach to attach serial joysticks
    attach_serial
  fi
  # DEBUG output
  if [ ${DEBUG} -ge 2 ]
  then
    echo "DEBUG: end configure_hardware"
  fi
}

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: after definition of configure_hardware"
fi

# display a radiolist to chose the connected joystick(s)
### begin of program execution ###

# initialize variables
GAMEPORTS=""
JOYSTICKS=""
INPUTATTACH=""
ANALOG_OPTIONS=""
MODULES=""
PC_GAMEPORT=0		# 0 for no, 1 for yes
SERIAL=0

# set a sensible path
PATH=/sbin:/usr/sbin:/bin:/usr/bin
# make sure that lsjoystick is in the PATH
if [ \( -n "${PATH_TO_LSJOYSTICK}" \) -a \
     \( -z "$(which lsjoystick 2> /dev/null)" \) -a \
     \( -x "${PATH_TO_LSJOYSTICK}/lsjoystick" \) ]
then
  export PATH="${PATH}:${PATH_TO_LSJOYSTICK}"
fi

# DEBUG output
if [ ${DEBUG} -ge 2 ]
then
  echo "DEBUG: checking for lsjoystick"
fi
# check for lsjoystick
if [ \( -z "$(which lsjoystick 2> /dev/null)" \) -a \
     \( ! -x ${PATH_TO_LSJOYSTICK}/lsjoystick \) ]
then
  echo "Error: lsjoystick not found, cannot display joystick list" > /dev/stderr
fi

# main loop
while true
do
  # present main menu
  # get function name to execute
  menu_item=$( \
  Xdialog --title "$(basename $0)" --stdout --separate-output --item-help \
  --ok-label Select --cancel-label Quit --no-tags --help "${helptext_main}" \
  --menubox \
  "chose action" 0 0 3 \
  "show_joystick_list" "show detected joysticks" "shows a list of all\
 joysticks already detected by the system" \
  "configure_hardware" "configure joystick hardware" "manually configure what\
 kinds of joysticks are connected")
  #\
  #"create_device_nodes" "create joystick devices" "creates 16 joystick devices
 #(only needed if you don't have udev or devfs and more joysticks than device nodes)" \
  #"unload_all_modules" "unload drivers" "unloads every joystick driver module" \
  #"exit 0" "quit" "quits joystick configuration program"
  #)
  case ${menu_item} in
    configure_hardware)
      # check for kernel 2.6.x
      if [ $(uname -r|sed 's/^[^.]*\.\([^.]*\)..*$/\1/') -ne 6 ]
      then
        echo "Warning: only configuration of kernel 2.6 is supported" \
	  > /dev/stderr
        Xdialog --title "$(basename $0)" --infobox \
	  "only configuration of kernel 2.6 supported" 0 0 10000
	continue
      fi
      # reset configuration
      GAMEPORTS=""
      PC_GAMEPORT=0
      SERIAL=0
      JOYSTICKS=""
      INPUTATTACH=""
      ANALOG_OPTIONS=""
      MODULES=""
      # execute function
      ${menu_item};;

    unload_all_modules)
      # check for kernel 2.6.x
      if [ $(uname -r|sed 's/^[^.]*\.\([^.]*\)..*$/\1/') -ne 6 ]
      then
        echo "Warning: only configuration of kernel 2.6 is supported" \
	  > /dev/stderr
        Xdialog --title "$(basename $0)" --infobox \
	  "only configuration of kernel 2.6 supported" 0 0 10000
	continue
      fi
      # execute function
      ${menu_item};;

    "")
      # quit
      exit 0;;

    *)
      # execute function
      ${menu_item};;
  esac
done
