#! /bin/bash
#    Copyright (c) 2012 - 2023 nerdopolis (or n3rdopolis) <bluescreen_avenger@verzion.net>
#
#    This file is part of RebeccaBlackOS.
#
#    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, see <http://www.gnu.org/licenses/>.


#This script is for configuring multiple seats in Weston. It matches the devices devpath, and the devices serial ID, and sets the ENV{WL_SEAT} and ENV{ID_SEAT} values, or the devices path ID and sets the ENV{ID_SEAT} value in a the udev file /etc/udev/rules.d/72-physical-and-logical-seats.rules
#This makes it easier for users to configure multiple pointers and keyboard focuses, as well as multiple sessions.

#Require root privlages
if [[ $UID != 0 ]]
then
  qarma --info --text="Must be run as root." --title="Multipointer and Seat Configuration" 2>/dev/null
  exit
fi

#Allow Exclamation points to be used in commands by turning off the history
set +H

#Specify the maximum number of seats. This is a more for usability, for specifying how many seats are listed in the qarma dialog. There can be more, but if there are too many, it become a hastle for the user
MaxNumberOfSeats=10

#get the current session ID and seat id
export XDG_SESSION_ID=$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1/session/auto org.freedesktop.login1.Session Id | awk -F \" '{print $2}')
export XDG_SEAT=$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1/session/auto org.freedesktop.login1.Session Seat | awk -F \" '{print $2}')

#Dont run qarma as root
if [[ ! -z $SUDO_USER ]]
then
  QARMACMD="runuser -u "$SUDO_USER" -m -- qarma"
else
  QARMACMD="qarma"
fi

#Function that prepares the list of seats for the dropdown menus
#It takes no arguments
function PrepareSeatList
{
  #Generate a list of logical seats for the Qarma dialogs for selection
  unset LogicalSeatList
  LogicalSeatList="|default"
  for (( SeatIterator=1; SeatIterator<MaxNumberOfSeats; SeatIterator++ ))
  do
    LogicalSeatList+="|"
    LogicalSeatList+="seat$SeatIterator"
  done

  #Generate a list of physical seats for the Qarma dialogs for selection
  unset PhysicalSeatArray

  PhysicalSeatArray=('')
  PhysicalSeatArray+=($(loginctl list-seats --no-legend | grep -v seat-vtty | sort))
  for (( SeatIterator=1; SeatIterator<MaxNumberOfSeats; SeatIterator++ ))
  do
    IncludeSeat=1
    for PhysicalSeat in "${PhysicalSeatArray[@]}"
    do
      if [[ "seat$SeatIterator" == $PhysicalSeat ]]
      then
        IncludeSeat=0
        break
      fi
    done
    if [[ $IncludeSeat == 1 ]]
    then
      PhysicalSeatArray+=("seat$SeatIterator")
    fi
  done
  unset PhysicalSeatList
  FirstPhysicalSeat=1
  for SeatElement in "${PhysicalSeatArray[@]}"
  do
    if [[ $FirstPhysicalSeat != 1 ]]
    then
      PhysicalSeatList+="|"
    fi
    PhysicalSeatList+=$SeatElement
    FirstPhysicalSeat=0
  done
}


#Command to enumerate device files
function EnumerateDeviceFiles
{
  find /sys/class/input/input[0-9]* /sys/class/drm/card[0-9]* /sys/class/sound/card[0-9]* ! -path "*-*" 
}

#Collect all device info
function CollectDeviceInfo
{
  NumberOfDevices=0
  unset DevicePaths
  unset DeviceDisplayNames
  unset DeviceNames
  unset DeviceConnections
  unset DeviceCurrentPhysicalSeatIDs
  unset DeviceCurrentLogicalSeatIDs
  unset DeviceTypes
  unset DeviceInputTypeFlags
  unset DeviceConnectTimestamps

  unset DeviceNewPhysicalSeatIDs
  unset DeviceNewLogicalSeatIDs
  unset DeviceNewSeatConfigChangeds
  #Get all input devices from udev, and store attributes into an array
  while read -r Device
  do
    NewDevicesFound=1
    #Handle the display of the device type to the user. Some devices report themselves as multiple types such as a transciver that multiple wireless devices can register with, will appear as "Mouse / Keyboard"
    unset DeviceType

    #get properties of the device
    DeviceProperties=$(udevadm info --query=property  --path=$Device)
    IFS=$'\n'
    DeviceProperties=($DeviceProperties)
    unset IFS

    unset DEV_ID_SEAT DEV_WL_SEAT DEV_ID_INPUT DEV_ID_INPUT_MOUSE DEV_ID_INPUT_TOUCHSCREEN DEV_ID_INPUT_TOUCHPAD DEV_ID_INPUT_KEYBOARD DEV_DEVPATH DEV_ID_SERIAL DEV_ID_PATH DEV_DEVNAME DEV_SUBSYSTEM DEV_USEC_INITIALIZED
    for DeviceProperty in ${DeviceProperties[@]}
    do
      IFS="="
      DeviceProperty=($DeviceProperty)
      unset IFS
      case ${DeviceProperty[0]} in
        ID_SEAT)
          DEV_ID_SEAT=${DeviceProperty[1]}
          ;;
        WL_SEAT)
          DEV_WL_SEAT=${DeviceProperty[1]}
          ;;
        ID_INPUT)
          DEV_ID_INPUT=${DeviceProperty[1]}
          ;;
        ID_INPUT_MOUSE)
          DEV_ID_INPUT_MOUSE=${DeviceProperty[1]}
          ;;
        ID_INPUT_TOUCHSCREEN)
          DEV_ID_INPUT_TOUCHSCREEN=${DeviceProperty[1]}
          ;;
        ID_INPUT_TOUCHPAD)
          DEV_ID_INPUT_TOUCHPAD=${DeviceProperty[1]}
          ;;
        ID_INPUT_KEYBOARD)
          DEV_ID_INPUT_KEYBOARD=${DeviceProperty[1]}
          ;;
        DEVPATH)
          DEV_DEVPATH=${DeviceProperty[1]}
          ;;
        ID_FOR_SEAT)
          DEV_ID_FOR_SEAT=${DeviceProperty[1]}
          ;;
        ID_SERIAL)
          DEV_ID_SERIAL=${DeviceProperty[1]}
          ;;
        ID_PATH)
          DEV_ID_PATH=${DeviceProperty[1]}
          ;;
        DEVNAME)
          DEV_DEVNAME=${DeviceProperty[1]}
          ;;
        SUBSYSTEM)
          DEV_SUBSYSTEM=${DeviceProperty[1]}
          ;;
        USEC_INITIALIZED)
          DEV_USEC_INITIALIZED=${DeviceProperty[1]}
          ;;
      esac
    done

    DevicePhysicalSeatID=$DEV_ID_SEAT
    DeviceLogicalSeatID=$DEV_WL_SEAT
    IsDeviceInput=$DEV_ID_INPUT
    IsDeviceMouse=$DEV_ID_INPUT_MOUSE
    IsDeviceTouchscreen=$DEV_ID_INPUT_TOUCHSCREEN
    IsDeviceTouchpad=$DEV_ID_INPUT_TOUCHPAD
    IsDeviceKeyboard=$DEV_ID_INPUT_KEYBOARD
    if [[ $IsDeviceInput == 1 ]]
    then
      IsDeviceInput=1
      DeviceConnection=$DEV_ID_PATH
      DeviceName=$DEV_ID_SERIAL
      DeviceDisplayName=$(udevadm info -q all --attribute-walk --path=$Device | sed -re '/=/!d' -e "s/^ *([a-zA-Z0-9])/\1/g" -e 's/==/=/g' -e 's/"//g' | awk -F ' *= *' '$1 == "ATTRS{name}" {print $0}' | cut -d = -f2- | tail -1 | sed 's/[^0-9a-zA-Z_ -]*//g')
    else
      IsDeviceInput=0
      DeviceConnection=$(echo $DEV_ID_FOR_SEAT |sed -e 's/^drm//g' -e 's/^graphics//g')
      DeviceName=$DEV_DEVNAME
      DeviceSubsystem=$DEV_SUBSYSTEM
      if [[ $DeviceSubsystem == "drm" ]]
      then
        DeviceType="KMS Graphics Card"
      fi
      if [[ $DeviceSubsystem == "graphics" ]]
      then
        DeviceType="Frame Buffer"
      fi
      if [[ $DeviceSubsystem == "sound" ]]
      then
        DeviceType="Sound Card"
      fi
      DeviceDisplayName=$(udevadm info -q all --attribute-walk --path=$Device | sed -re '/=/!d' -e "s/^ *([a-zA-Z0-9])/\1/g" -e 's/==/=/g' -e 's/"//g' | awk -F ' *= *' '$1 == "DRIVERS" {print $0}' | cut -d = -f2- | head -1 | sed 's/[^0-9a-zA-Z_ -]*//g')
      DeviceDisplayName+=" $Device"
    fi
    #if the device doesn't have a serial ID, which usually tells the make and model, then display it as 'generic' to the user
    if [[ -z $DeviceDisplayName ]]
    then
      DeviceDisplayName=$(echo $DeviceName| sed 's/[^0-9a-zA-Z_ -]*//g')
      if [[ $DeviceDisplayName == "noserial" ]]
      then
        DeviceDisplayName="Generic"
      fi
    fi

    #if the device has no configured WL_SEAT value, then weston sees it as it being under the default seat
    if [[ -z $DeviceLogicalSeatID ]]
    then
      DeviceLogicalSeatID="default"
    fi

    #if the device has no configured ID_SEAT value, then udev sees it as it being under the seat0 seat
    if [[ -z $DevicePhysicalSeatID ]]
    then
      DevicePhysicalSeatID="seat0"
    fi

    #if the device is reported as a mouse, then add that to the device type string. if the device type string has a previous device type in it, prepend a / for the user
    if [[ -z $IsDeviceMouse ]]
    then
      IsDeviceMouse=0
    else
      if [[ ! -z $DeviceType ]]
      then
        DeviceType+=" / "
      fi
      DeviceType+="Mouse"
    fi

    #if the device is reported as a touchscreen, then add that to the device type string. if the device type string has a previous device type in it, prepend a / for the user
    if [[ -z $IsDeviceTouchscreen ]]
    then
      IsDeviceTouchscreen=0
    else
      if [[ ! -z $DeviceType ]]
      then
        DeviceType+=" / "
      fi
    DeviceType+="Touchscreen"
    fi

    #if the device is reported as a touchpad (as in a laptop touchpad), then add that to the device type string. if the device type string has a previous device type in it, prepend a / for the user
    if [[ -z $IsDeviceTouchpad ]]
    then
      IsDeviceTouchpad=0
    else
      if [[ ! -z $DeviceType ]]
      then
        DeviceType+=" / "
      fi
    DeviceType+="Touchpad"
    fi

    #if the device is reported as a keyboard, then add that to the device type string. if the device type string has a previous device type in it, prepend a / for the user
    if [[ -z $IsDeviceKeyboard ]]
    then
      IsDeviceKeyboard=0
    else
      if [[ ! -z $DeviceType ]]
      then
        DeviceType+=" / "
      fi
    DeviceType+="Keyboard"
    fi

    #if DeviceType exits, then add to the arrays. DeviceType only exists if the device is an input device, or graphics device. All other devices reported by udev, it will be empty and ignored. These devices are the only ones that concern weston multiseat, or udev multiseat.
    DeviceConnectionDuplicate=0
    for (( CurrentDeviceNumber=1; CurrentDeviceNumber<=NumberOfDevices; CurrentDeviceNumber++ ))
    do
      DeviceDuplicateTest=${DeviceConnections[$CurrentDeviceNumber]}
      if [[ $DeviceDuplicateTest == $DeviceConnection ]]
      then
        DeviceConnectionDuplicate=1
        if [[ $IsDeviceInput == 0 ]]
        then
          DeviceTypes[$CurrentDeviceNumber]+="/$DeviceType"
          DeviceDisplayNames[$CurrentDeviceNumber]+=", $Device"
        fi
      fi
    done
    if [[ ! -z $DeviceType && $DeviceConnectionDuplicate == 0 ]]
    then
      ((NumberOfDevices++))
      DevicePaths[$NumberOfDevices]=$Device
      DeviceDisplayNames[$NumberOfDevices]=$DeviceDisplayName
      DeviceNames[$NumberOfDevices]=$DeviceName
      DeviceConnections[$NumberOfDevices]=$DeviceConnection
      DeviceCurrentPhysicalSeatIDs[$NumberOfDevices]=$DevicePhysicalSeatID
      DeviceCurrentLogicalSeatIDs[$NumberOfDevices]=$DeviceLogicalSeatID
      DeviceTypes[$NumberOfDevices]=$DeviceType
      DeviceInputTypeFlags[$NumberOfDevices]=$IsDeviceInput
      DeviceConnectTimestamps[$NumberOfDevices]=$DEV_USEC_INITIALIZED

      DeviceNewPhysicalSeatIDs[$NumberOfDevices]=$DevicePhysicalSeatID
      DeviceNewLogicalSeatIDs[$NumberOfDevices]=$DeviceLogicalSeatID
      DeviceNewSeatConfigChangeds[$NumberOfDevices]=0
     fi

  done < <(EnumerateDeviceFiles)
}

#Present a list of devices to select to associate to desired seats
function DeviceSelection
{
  DeviceSortArrayString=""
  for (( DeviceIterator=1; DeviceIterator <= $NumberOfDevices; DeviceIterator++ ))
  do
    if [[ $DeviceIterator != 1 ]]
    then
      DeviceSortArrayString+=$'\n'
    fi
    DeviceSortArrayString+="$DeviceIterator,${DeviceConnectTimestamps[$DeviceIterator]}"
  done
  DeviceSortArrayIndex=($(echo "$DeviceSortArrayString" | sort -rnt "," -k 2,2 | awk -F "," '{print $1}'))


  DeviceSelectionArray=()
  DeviceConnectionOrder=$NumberOfDevices
  for DeviceElementNumber in "${DeviceSortArrayIndex[@]}"
  do
    DeviceSelectionArray+=("$DeviceElementNumber")
    DeviceSelectionArray+=("${DeviceTypes[$DeviceElementNumber]}")
    DeviceSelectionArray+=("${DeviceNames[$DeviceElementNumber]}")
    DeviceSelectionArray+=("${DeviceConnections[$DeviceElementNumber]}")
    if [[ ${DeviceInputTypeFlags[$DeviceElementNumber]} == 1 ]]
    then
      DeviceSelectionArray+=("${DeviceCurrentPhysicalSeatIDs[$DeviceElementNumber]} / ${DeviceCurrentLogicalSeatIDs[$DeviceElementNumber]}")
      DeviceSelectionArray+=("${DeviceNewPhysicalSeatIDs[$DeviceElementNumber]} / ${DeviceNewLogicalSeatIDs[$DeviceElementNumber]}")
    else
      DeviceSelectionArray+=("${DeviceCurrentPhysicalSeatIDs[$DeviceElementNumber]}")
      DeviceSelectionArray+=("${DeviceNewPhysicalSeatIDs[$DeviceElementNumber]}")
    fi
    DeviceSelectionArray+=("$DeviceConnectionOrder")
    ((DeviceConnectionOrder--))
  done

  DeviceSelectionArray+=(-1 "[Apply Changes]" '' '' '' '' '')
  DeviceSelectionArray+=(-2 "[Cancel Changes]" '' '' '' '' '')

  $QARMACMD --height 580 --width 800 --list --hide-column 1 --column=selectionnumber --column="Type" --column="Name" --column="Connection Path" --column="Current Physical/Logical Seats" --column="New Physical/Logical Seats" --column="Connected Order" --title="Multipointer and Seat Configuration" --text="The changes made here will impact all users, as well as the login greeters. 
Multipointer is not supported by all Wayland desktops like it is on Weston.

This assist in associating devices with physical seats and logical seats.
   -  Physical 'Seats' are a grouping containing at least of graphics device 'card', and input devices.
      Having multiple physical seats allows one computer to act as multiple workstations.

   -  Logical 'Seats' are what belong to a separate mouse cursor and keyboard focus.
      The more logical seats with devices associated with them, the more pointers there will be.

Devices are associated with the physical seat first, then the logical seat. There can be multiple physical seats with multiple logical seats each.

This utility stores the device seat association with these following attributes:
   1. The device's internal name, usually containing the make and model.
   2. The port they are plugged into. Devices will only associate with the proper seats if plugged in the same way.

The default seat for physical seats is 'seat0', and the default seat for logical seats is 'default',

Please note that some devices have multiple internal components, and sometimes appear as 'different' devices

Devices are sorted by the order they were connected, with the newest devices on top" "${DeviceSelectionArray[@]}" 2> /dev/null
}

#Configure the seats of a selected device, it takes one argument, the array index element number of the device to configure
function ConfigureDevice
{
  CurrentDeviceNumber=$1
  unset NewPhysicalSeatNumber
  unset NewLogicalSeatNumber

  if [[ ${DeviceInputTypeFlags[$CurrentDeviceNumber]} == 1 ]]
  then
    NewSeatSelection=$($QARMACMD  --forms --add-combo "Physical Seat" --combo-values="$PhysicalSeatList" --add-combo "Logical Seat" --combo-values="$LogicalSeatList" --text="Multipointer and Seat Configuration

    Select a seat for ${DeviceTypes[$CurrentDeviceNumber]}: ${DeviceDisplayNames[$CurrentDeviceNumber]}
    Connected to: ${DeviceConnections[$CurrentDeviceNumber]}

    Current Physical Seat: ${DeviceCurrentPhysicalSeatIDs[$CurrentDeviceNumber]}
    Current Logical Seat: ${DeviceCurrentLogicalSeatIDs[$CurrentDeviceNumber]}" --separator="@" --title "Multipointer and Seat Configuration" 2>/dev/null)
    QarmaResult=$?
  else
    NewSeatSelection=$($QARMACMD --forms --add-combo "Physical Seat" --combo-values="$PhysicalSeatList" --text="Multipointer and Seat Configuration

    Select a seat for ${DeviceTypes[$CurrentDeviceNumber]}: ${DeviceDisplayNames[$CurrentDeviceNumber]}
    Connected to: ${DeviceConnections[$CurrentDeviceNumber]}

    Current Physical Seat: ${DeviceCurrentPhysicalSeatIDs[$CurrentDeviceNumber]}" --separator="@" --title "Multipointer and Seat Configuration" 2>/dev/null)
    QarmaResult=$?
  fi

  if [[ $QarmaResult != 0 ]]
  then
    return 1
  fi

  IFS="@"
  NewSeatSelection=($NewSeatSelection)
  unset IFS
  NewPhysicalSeatNumber=(${NewSeatSelection[0]})
  NewLogicalSeatNumber=(${NewSeatSelection[1]})

  #only change the seat id in udev if the user specified to do so
  if [[ ! -z ${NewSeatSelection[0]} || ! -z ${NewSeatSelection[1]} ]]
  then
    #If only the physical seat is specified
    if [[ -z $NewLogicalSeatNumber ]]
    then
      NewLogicalSeatNumber="${DeviceCurrentLogicalSeatIDs[$CurrentDeviceNumber]}"
    fi
    #If only the logical seat is specified
    if [[ -z $NewPhysicalSeatNumber ]]
    then
      NewPhysicalSeatNumber="${DeviceCurrentPhysicalSeatIDs[$CurrentDeviceNumber]}"
    fi

    #Don't reconfigure if the configured seats are the same values as the current ones
    if [[ ${DeviceCurrentPhysicalSeatIDs[$CurrentDeviceNumber]} != $NewPhysicalSeatNumber || ${DeviceNewLogicalSeatIDs[$CurrentDeviceNumber]} != $NewLogicalSeatNumber ]]
    then
      DeviceNewPhysicalSeatIDs[$CurrentDeviceNumber]=$NewPhysicalSeatNumber
      DeviceNewLogicalSeatIDs[$CurrentDeviceNumber]=$NewLogicalSeatNumber
      DeviceNewSeatConfigChangeds[$CurrentDeviceNumber]=1
    else
      #Account for the device being set to different seats, then the user setting them back
      DeviceNewPhysicalSeatIDs[$CurrentDeviceNumber]=${DeviceCurrentPhysicalSeatIDs[$CurrentDeviceNumber]}
      DeviceNewLogicalSeatIDs[$CurrentDeviceNumber]=${DeviceNewLogicalSeatIDs[$CurrentDeviceNumber]}
      DeviceNewSeatConfigChangeds[$CurrentDeviceNumber]=0
    fi
  fi
}

#Function that writes the new changes to the config files
function CommitConfigFile
{
  #Create the config file
  touch /etc/udev/rules.d/72-physical-and-logical-seats.rules


  #Go through each probed input device and prompt for a the seata to add the device to
  for (( CurrentDeviceNumber=1; CurrentDeviceNumber<=NumberOfDevices; CurrentDeviceNumber++ ))
  do

    #only change the seat id in udev if the user specified to do so
    if [[ ${DeviceNewSeatConfigChangeds[$CurrentDeviceNumber]} == 1 ]]
    then

      NewPhysicalSeatNumber=${DeviceNewPhysicalSeatIDs[$CurrentDeviceNumber]}
      NewLogicalSeatNumber=${DeviceNewLogicalSeatIDs[$CurrentDeviceNumber]}
      cat /etc/udev/rules.d/72-physical-and-logical-seats.rules > /tmp/72-physical-and-logical-seats.rules.work
      if [[ ${DeviceInputTypeFlags[$CurrentDeviceNumber]} == 1 ]]
      then
        if [[ ! -z ${DeviceNames[$CurrentDeviceNumber]} && ! -z ${DeviceConnections[$CurrentDeviceNumber]} ]]
        then
          awk "!/\"${DeviceNames[$CurrentDeviceNumber]//\//\\/}\"/ || !/\"${DeviceConnections[$CurrentDeviceNumber]//\//\\/}\"/" /tmp/72-physical-and-logical-seats.rules.work > /etc/udev/rules.d/72-physical-and-logical-seats.rules
        else
          if [[ ! -z ${DeviceNames[$CurrentDeviceNumber]} ]]
          then
            awk "!/${DeviceNames[$CurrentDeviceNumber]}//\//\\/}//" /tmp/72-physical-and-logical-seats.rules.work > /etc/udev/rules.d/72-physical-and-logical-seats.rules
          fi
          if [[ ! -z ${DeviceConnections[$CurrentDeviceNumber]} ]]
          then
            awk "!/${DeviceConnections[$CurrentDeviceNumber]//\//\\/}/" /tmp/72-physical-and-logical-seats.rules.work > /etc/udev/rules.d/72-physical-and-logical-seats.rules
          fi
        fi
        echo "ENV{ID_SERIAL}==\"${DeviceNames[$CurrentDeviceNumber]}\", ENV{ID_PATH}==\"${DeviceConnections[$CurrentDeviceNumber]}\", ENV{ID_SEAT}=\"$NewPhysicalSeatNumber\", ENV{WL_SEAT}=\"$NewLogicalSeatNumber\"" >> /etc/udev/rules.d/72-physical-and-logical-seats.rules
      else
        if [[ ! -z ${DeviceConnections[$CurrentDeviceNumber]} ]]
        then
          awk "!/\"\*${DeviceConnections[$CurrentDeviceNumber]//\//\\/}\"/" /tmp/72-physical-and-logical-seats.rules.work > /etc/udev/rules.d/72-physical-and-logical-seats.rules
        fi
        echo "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"*${DeviceConnections[$CurrentDeviceNumber]}\", ENV{ID_SEAT}=\"$NewPhysicalSeatNumber\"" >> /etc/udev/rules.d/72-physical-and-logical-seats.rules
      fi
      rm /tmp/72-physical-and-logical-seats.rules.work
    fi
  done
}

#Function that sends the signal to udev and to the waylandloginmanagers to now detect the config changes that have been made
function SignalCompletion
{

  $QARMACMD --info --text "Seat Configuration for all devices is now complete.

Press OK to apply all the changes. This will switch user to the loginmanager display.
This is to signal the display server to detect the new changes, and the waylandloginmanager to detect new seats.

Your sessions will NOT be terminated, but they will need to be switched back into with Switch User" --no-wrap --title="Multipointer and Seat Configuration" 2>/dev/null

  #Reload the new configuration into udev
  udevadm control --reload-rules


  #Notify each input device about the change with udev, instead of doing a full trigger on all devices on the system
  for (( Itr=1; Itr<5; Itr++ ))
  do
    udevadm trigger --subsystem-match=input
    udevadm trigger --subsystem-match=drm
    udevadm trigger --subsystem-match=graphics
    udevadm trigger --subsystem-match=sound
    sleep 1
  done

  #Force the server to pickup the changes
  waylandloginmanager --sendcommand DetectSeats
  sleep 1
  RunningSeats=($(loginctl --no-legend list-seats 2>/dev/null | sort))
  for RunningSeat in ${RunningSeats[@]}
  do
    if [[ $RunningSeat == $XDG_SEAT ]]
    then
      waylandloginmanager --sendcommand Switch $RunningSeat
    else
      waylandloginmanager --sendcommand Change $RunningSeat
    fi
  done
}

#Function that shows more detailed help
function HelpDialog
{
echo "
INTRODUCTION
============

What is a seat? 
---------------
A seat is a combination of devices on a computer that belong 
to one user. Most computers have one seat each. However this
wizard allows multiple seats to be configured with proper hardware.

What is a Physical Seat?
-----------------------
A physical seat is a group of hardware, needing a different 
video card each to work, along with input devices, such as a 
keyboard or mouse. These devices belong to different sessions,
allowing each user to have independent control over their own
session each. If such a configuration was set up in a manor
that the cables and computer were well hidden such a setup
could appear to the unwitting eye as two (or more) computers,
where all seats are really being run on one.

A USB graphics card is usually suitable for the multiple
graphics card requirement, as long as the driver is supported.

What is a Logical Seat?
-----------------------
A logical seat is a group of hardware within a physical seat 
that are shared within a session, to where, when supported by
a given desktop environment, allows there to be multiple
pointers on one session, along with their keyboards and mice. 
This type of configuration allows for better collaboration,
with two users sharing a session. It is also possible to 
have physical seats, with multiple logical seats, to where each
independent session also has multiple pointers.

A special graphics configuration for logical seats/pointers is not needed.

ABOUT THIS UTILITY
==================
This utility simplifies the configuration of the seats. 
Typically one could edit the configuration by editing udev
config files manually. This utility assists in creating 
and editing all seat config, and storing it in the file
/etc/udev/rules.d/72-physical-and-logical-seats.rules

Devices, and their configured seat values are stored by 
the name that the device presents itself as (if availible)
and the connection.

This utility handles the configuration for input devices
and for video and sound cards

Input Devices can be assignged to physical seats, and 
logical seats, identified in the config by their
ID_SERIAL and ID_PATH properties

Output devices (like video and sound cards) can only be
assigned to physical seats, as assigning them to a logical
seat has no impact on how multipointer works.


USING THIS UTILITY
==================
The utility will present a list of devices. Select the desired devices,
then associate them with the desired seat.

The devices are sorted by the order they were connected. Newest devices are on top.
The device plugin time is queried the same way the other attriubtes are, and it is
relative to the boot time. No actions are needed to ensure that the device plugin
order is determined by this utility correctly.

Leaving a field blank leaves the device assigned to the current seat info.
For example, if a device is on seat0/default and the logical seat is set to seat1
the device's physical seat will remain on seat0 as the logical seat is set to seat1.

"|$QARMACMD --title="Multipointer and Seat Configuration"  --text-info --font=mono --width=700 --height=600 2>/dev/null
}

#Function for the main configuration wizard, it takes no arguments
function MainConfig
{
  while [ 1 ]
  do
    ChangedDeviceCount=0

    SelectedDevice=$(DeviceSelection)
    Result=$?

    #Exit if selected
    if [[ $Result != 0 || $SelectedDevice == -2 ]]
    then
      $QARMACMD --question --no-wrap --title="Multipointer and Seat Configuration" --text "Are you sure you want to quit this utility without saving changes?" 2>/dev/null
      Result=$?
      if [[ $Result == 0 ]]
      then
        break
      else
        continue
      fi
    fi

    if [[ $SelectedDevice == "" ]]
    then
      AdditionalPromptText="No device selected, assuming save and close."$'\n'
      SelectedDevice=-1
    else
      AdditionalPromptText=""
    fi

    #Break if user is not selecting any more devices
    if [[ $SelectedDevice == -1 ]]
    then
      DeviceSortArrayString=""
      for (( DeviceIterator=1; DeviceIterator <= $NumberOfDevices; DeviceIterator++ ))
      do
        if [[ $DeviceIterator != 1 ]]
        then
          DeviceSortArrayString+=$'\n'
        fi
        DeviceSortArrayString+="$DeviceIterator,${DeviceConnectTimestamps[$DeviceIterator]}"
      done
      DeviceSortArrayIndex=($(echo "$DeviceSortArrayString" | sort -rnt "," -k 2,2 | awk -F "," '{print $1}'))

      NewDeviceConfigPromptText=""
      for DeviceElementNumber in "${DeviceSortArrayIndex[@]}"
      do

        #only change the seat id in udev if the user specified to do so
        if [[ ${DeviceNewSeatConfigChangeds[$DeviceElementNumber]} == 1 ]]
        then
          ((ChangedDeviceCount++))
          NewDeviceConfigPromptText+="${DeviceTypes[$DeviceElementNumber]}:  ${DeviceDisplayNames[$DeviceElementNumber]} on ${DeviceConnections[$DeviceElementNumber]}"$'\n'
          if [[ ${DeviceInputTypeFlags[$DeviceElementNumber]} == 1 ]]
          then
            NewDeviceConfigPromptText+="     From: ${DeviceCurrentPhysicalSeatIDs[$DeviceElementNumber]} / ${DeviceCurrentLogicalSeatIDs[$DeviceElementNumber]}"$'\n'
            NewDeviceConfigPromptText+="     To: ${DeviceNewPhysicalSeatIDs[$DeviceElementNumber]} / ${DeviceNewLogicalSeatIDs[$DeviceElementNumber]}"$'\n'
          else
            NewDeviceConfigPromptText+="     From: ${DeviceCurrentPhysicalSeatIDs[$DeviceElementNumber]}"$'\n'
            NewDeviceConfigPromptText+="     To: ${DeviceNewPhysicalSeatIDs[$DeviceElementNumber]}"$'\n'
          fi
        fi
      done
      if [[ $ChangedDeviceCount == 0 ]]
      then
        $QARMACMD --forms --ok-label=Yes --cancel-label=No --title="Multipointer and Seat Configuration" --text "${AdditionalPromptText}Are you sure you want to quit this utility without making changes?" 2>/dev/null
        Result=$?
        if [[ $Result == 0 ]]
        then
          break
        else
          continue
        fi
      fi
      $QARMACMD --forms --ok-label=Yes --cancel-label=No  --title="Multipointer and Seat Configuration" --text "${AdditionalPromptText}Are you sure you want to quit this utility and save the following changes to the $ChangedDeviceCount Devices?"$'\n'$'\n'"$NewDeviceConfigPromptText" 2>/dev/null
      Result=$?
      if [[ $Result == 0 ]]
      then
        break
      else
        continue
      fi
    fi

    ConfigureDevice $SelectedDevice
  done

  if [[ $ChangedDeviceCount == 0 ]]
  then
    return 1
  else
    return 0
  fi
}


function MainDialog
{
SelectedOption=$($QARMACMD --height 380 --list --hide-column 1 --column=selectionnumber --column=option --hide-header --title="Multipointer and Seat Configuration" --text "Select device configuration mode.

'Configure All Devices' lists all devices currently attached to be configured.

'Show Detailed Help' shows more details on how to use this wizard

'Quit' Exits this wizard
" "1" "Configure Devices" "2" "Show Detailed Help" "3" "Quit")
Return=$?
if [[ $Return != 0 ]]
then
  exit
fi
}


PrepareSeatList
#Main wizard dialog
while [ 1 ]
do
  MainDialog
  if [[ $SelectedOption == 1 ]]
  then
    CollectDeviceInfo
    MainConfig
    Return=$?
    #Only act if settings have been changed
    if [[ $Return == 0 ]]
    then
      CommitConfigFile
      SignalCompletion
    fi
  elif [[ $SelectedOption == 2 ]]
  then
    HelpDialog
  elif [[ $SelectedOption == 3 ]]
  then
    exit
  fi
done
