In some situations, there may be a need for multiple mice on a system. For example, at a conference when using a system to discuss details relating to the company. The speaker may need a mouse to point things out, while others can use another mouse to point out other things.
There is also a way to include the keyboard as well, but this is not part of the BASH script I wrote. You can always add in a keyboard if needed. And yes, if you have open two word processor apps, one keyboard can type in one, while another person uses the other to type on another. If you have a system with multiple monitors, then you can produce two workstations with one system. The best way is to have two monitors, each with their own menu option.
Now, really to throw in a little more information, we can make it three keyboards and mice, or more.
Prerequisites
I will admit that I only tested this on Ubuntu, but I'm sure it will work with other systems as long as you are using XORG. This will not work on Wayland with these commands.
The automated script can find two mice. If it finds only one or more than two, it will exit with an error. The only devices it looks for are a mouse and a trackball. I did not include touchpads, but you can add this if needed.
Of course, the system must be X11 and not Wayland.
The script relies on the program 'test' as well as 'xinput'. If you are running X11, then you should have 'xinput' already installed. If it is not present, you can use your software manager to install it, such as on Ubuntu:
For other distros, you will need to change 'apt' to your software manager program.
For the script, we need to go over eight sections:
Since you will need the 'xinput' command, but only works on X11, we must make sure X11 is the current Windowing System.
To check this, we can simply check the environment variable 'XDG_SESSION_TYPE' and see that it is 'x11'.
I have tested the environment variable on the following systems:
This checks that the system is not 'x11' and will then echo an error message and exit with an error code.
NOTE: I will attach the full script at the end of the article.
Check for the 'test' program
The script uses some parameters for the 'if' statement, which uses the 'test' program. Most times, the 'test' app is present, but it is best to check:
This basically runs the command 'test --version' and sends all output to the '/dev/null' device so you’ll see nothing on the screen. The second line places the exit code into the variable 'out1'. We then test whether the exit code is not equal to zero. If the exit code is not zero, then this would mean that the program does not exist. So, if the program does not exist, then the script will manage the issue.
Now, the command to test the existence of 'test' could also be:
When using these parameters with an 'if' statement, these are using the 'test' program. There are other parameters that we can use:
Also, the code includes the exclamation mark (!), which means 'not'. So, in this case we test the file does not exist, and if it doesn't, we can throw an error message and exit with a specific code.
Determine the presence of ‘xinput’ app
We can find out if the 'xinput' app is present on the current system by using an 'if' statement and the parameter '-e', just like we did with 'test'.
Again, we are testing for the 'not' with an exclamation mark. Since we are checking for the existence of the file, we can check for it not existing and then exit with an error code.
As you can see with the testing of the existence of a file, you need to give the full path to the file. I find the file using the command 'whereis'. All you do is pass the name of the file, so for 'test', the command is:
When I run the command, my output is like that in Figure 1.
FIGURE 1
You can see that the first file listed is the one we want, while the second is for the 'man' page.
Search for Up to 2 Mice
Now we come to the heart of the script. This is where we get into more detailed BASH code. We are simply editing the output of a command and taking the output to find the information.
To set up the section for finding mice and trackballs, I need to set up a few variables. Here, we set 'dev_count' to zero. The variable keeps track of the number of devices found. We also need to set two variables to an empty string. These are called 'mouse1' and mouset1'.
The next step for variables is to search for any mice connected to the system. To do this, we use the following code:
In this command, we run the command 'xinput list' to get a list of the input devices managed by X11. An example of the 'xinput list' command is in Figure 2. The list should include mice, keyboards, trackballs and touchpads. We then pipe the entire list through the 'grep' command to find any lines with the word 'mouse' in it. This list is then piped again through 'grep' to narrow the list down to those that are marked as 'pointer'. Now, the variable 'temp1' contains all the mice connected to the system.
FIGURE 2
We then check the list to make sure it is not empty. If it is empty, then there are no mice connected to the system.
If the variable has content, then we place the value into the variable 'mouse1'.
Next, we do the same for 'Trackball' and place the value in the variable 'mouset1'.
So, now we can look into the code of separating out the information we need.
The Mouse and Trackball sections are the same, and I will only cover one section. The Trackball section just uses different variables for storing the ID numbers of the trackballs.
We take the variable 'mouse1' and place it into the variables 'm1' and 'm2'.
Next, we set the variable 'char' to an equal sign. This is the character we are searching for in the variable. The portion we are interested in is the small section 'ID=##'. It is the numbers that we want. The quickest way is to search for the equal sign.
Next, we perform a search for the equal sign and make a count of how many there are. Each equal sign found we place into the variable 'filtered_string':
Now, we can count the length of the variable to determine how many equal signs are in it:
The result we put into the variable 'count'.
Next, we see if 'count' is greater than or equal to '1'. If the count is 1 or more, then we know there is a mouse we can use.
Now we can take the variable 'm1' and get the two characters after each equal sign and place it in the variable 'm2'. The value in 'm2' is then stripped of spaces on the left side, leaving the value placed into the variable 'temp'. Next, the spaces are stripped from the right side of the value and then the result is placed into the variable 'm1'. The result of the variable is the IDs of the mice separated by a newline.
We then check to see if the length of the value in the variable 'm1' is greater than 3. If the length is greater than 3, then there should be two IDs in the variable. We then place the value in the variable 'm1' into 'temp'.
We look at the value in 'temp' and place the first line into the variable 'm1' and the second line into 'm2'. We then add '2' to the 'dev_count' since we just added two mice.
If the length of the value in 'm1' is less than 3, this means there is only one mouse found and is currently in the variable 'm1', so we clear the variable 'm2'. We also increase the value in 'dev_count' by 1, since we added one mouse.
Check for up to 2 Trackballs
The next section is to check for trackballs. We use the same process, except that the IDs are placed into 't1' and 't2'.
The value searched for is not 'Mouse' but 'Trackball', which is in 'mouset1'.
If you are using a laptop with a touchpad, you can change 'Trackball' to 'TouchPad', if you would prefer to use it. It is possible to copy the section and after pasting it, change it to 'TouchPad' so you can check for all three types.
Determine if There are More, or Less, than 2 Pointing Devices
This is an easy section since we kept count of the devices as went into the variable 'dev_count'.
We check if the value of 'dev_count' is not '2'. If the count is not equal to two, we give an error message and exit with a specific exit code.
Find the Second Pointing Device
Now, we will assume the first pointing device we find in our list will be the first pointing device.
Let's create an example. We say that we have two mice hooked to the PC and that the variables 'm1' and 'm2' contain the IDs of the mice. We can assume that 'm1' is the mouse we will use as the main mouse and 'm2' is the secondary mouse. This is the same, however we find the IDs.
So, we look through each variable in the order of 'm1', 'm2', 't1' and 't2'. If the variable has a value in it, a length greater than zero, we set the variable 'found' equal to 1. If we find a variable that has a length greater than 0 and the variable 'found' is set to '1'. we can use that ID as our second mouse. The ID is then placed into the variable 'use'.
Once we have the variable in 'use', we can then run the last command that will set it up as its own cursor.
Initialize Second Mouse as a Separate Cursor
This section is the whole reason for the script. These last two commands create a second mouse pointer and then connect the second mouse to the new secondary mouse cursor.
The first thing to do is create the secondary cursor with the command:
Now that we have a cursor set, we can attach a mouse to the cursor with ID we found with the command:
The mouse should now move a second cursor that is on the screen. The new cursor is independent of the original cursor controlled by the other mouse.
Keep in mind that you can also attach a keyboard as well. If you have a keyboard and mouse connected to one USB dongle, you will want to make sure you use the ID for the set when connecting them if you want to set up both. The command for a keyboard is:
Again, make sure you use the correct ID.
If you want to stop the mapping, look at the 'xinput list' and find the line listed as 'SecondaryCursor' to get the ID. Use the ID in the command:
The mapping should be stopped if you remove the mouse/keyboard or definitely will be if you reboot the system.
NOTE: The complete script is attached. It is named 'mouse.txt'. Simply rename it to 'mouse.sh' and then make it executable.
Conclusion
This can be handy if you need a system with multiple mice and keyboards. If you add the 'xinput' commands to the '.xinitrc' file, the system should keep the mapping persistent.
This may be one of those things you need to try to see how it works, and I'm sure you can come up with your own uses for this command.
There is also a way to include the keyboard as well, but this is not part of the BASH script I wrote. You can always add in a keyboard if needed. And yes, if you have open two word processor apps, one keyboard can type in one, while another person uses the other to type on another. If you have a system with multiple monitors, then you can produce two workstations with one system. The best way is to have two monitors, each with their own menu option.
Now, really to throw in a little more information, we can make it three keyboards and mice, or more.
Prerequisites
I will admit that I only tested this on Ubuntu, but I'm sure it will work with other systems as long as you are using XORG. This will not work on Wayland with these commands.
The automated script can find two mice. If it finds only one or more than two, it will exit with an error. The only devices it looks for are a mouse and a trackball. I did not include touchpads, but you can add this if needed.
Of course, the system must be X11 and not Wayland.
The script relies on the program 'test' as well as 'xinput'. If you are running X11, then you should have 'xinput' already installed. If it is not present, you can use your software manager to install it, such as on Ubuntu:
Code:
sudo apt install xinput
For other distros, you will need to change 'apt' to your software manager program.
For the script, we need to go over eight sections:
- Test that X11 is being used
- Check for the 'test' program
- Determine the presence of 'xinput' app
- Search for up to 2 mice
- Check for up to 2 Trackballs
- Determine whether there are more or fewer than 2 pointing devices
- Find the second pointing device
- Initialize the second mouse as a separate cursor
Since you will need the 'xinput' command, but only works on X11, we must make sure X11 is the current Windowing System.
To check this, we can simply check the environment variable 'XDG_SESSION_TYPE' and see that it is 'x11'.
I have tested the environment variable on the following systems:
- Ubuntu
- CentOS
- Arch
- Fedora
- OpenSUSE
Code:
if [ "$XDG_SESSION_TYPE" != "x11" ]; then
This checks that the system is not 'x11' and will then echo an error message and exit with an error code.
NOTE: I will attach the full script at the end of the article.
Check for the 'test' program
The script uses some parameters for the 'if' statement, which uses the 'test' program. Most times, the 'test' app is present, but it is best to check:
Code:
test --version &> /dev/null
out1="$(echo $?)"
if [ $out1 -ne 0 ]; then
This basically runs the command 'test --version' and sends all output to the '/dev/null' device so you’ll see nothing on the screen. The second line places the exit code into the variable 'out1'. We then test whether the exit code is not equal to zero. If the exit code is not zero, then this would mean that the program does not exist. So, if the program does not exist, then the script will manage the issue.
Now, the command to test the existence of 'test' could also be:
Code:
if [ ! -e /usr/bin/test ]; then
When using these parameters with an 'if' statement, these are using the 'test' program. There are other parameters that we can use:
- -e file - checks file existence
- -f file - checks if file exists and is a regular file
- -d directory - checks of a directory exists
- -s file - checks if the file exists and has a size greater than zero
- -r file - checks if the file exists and that it is readable by the current user
- -w file - checks if the file exists and can be written to by the current user
- -x file - checks if the file exists and is executable by the current user
- -z variable - tests if the variable is empty
- file1 -nt file2 - checks if file1 is newer than file2
- file1 -ot file2 - checks if file1 is older than file2
Also, the code includes the exclamation mark (!), which means 'not'. So, in this case we test the file does not exist, and if it doesn't, we can throw an error message and exit with a specific code.
Determine the presence of ‘xinput’ app
We can find out if the 'xinput' app is present on the current system by using an 'if' statement and the parameter '-e', just like we did with 'test'.
Code:
if [ ! -e /usr/bin/xinput ]; then
Again, we are testing for the 'not' with an exclamation mark. Since we are checking for the existence of the file, we can check for it not existing and then exit with an error code.
As you can see with the testing of the existence of a file, you need to give the full path to the file. I find the file using the command 'whereis'. All you do is pass the name of the file, so for 'test', the command is:
Code:
whereis test
When I run the command, my output is like that in Figure 1.
FIGURE 1
You can see that the first file listed is the one we want, while the second is for the 'man' page.
Search for Up to 2 Mice
Now we come to the heart of the script. This is where we get into more detailed BASH code. We are simply editing the output of a command and taking the output to find the information.
To set up the section for finding mice and trackballs, I need to set up a few variables. Here, we set 'dev_count' to zero. The variable keeps track of the number of devices found. We also need to set two variables to an empty string. These are called 'mouse1' and mouset1'.
The next step for variables is to search for any mice connected to the system. To do this, we use the following code:
Code:
temp1="$(xinput list | grep 'mouse' | grep 'pointer')"
In this command, we run the command 'xinput list' to get a list of the input devices managed by X11. An example of the 'xinput list' command is in Figure 2. The list should include mice, keyboards, trackballs and touchpads. We then pipe the entire list through the 'grep' command to find any lines with the word 'mouse' in it. This list is then piped again through 'grep' to narrow the list down to those that are marked as 'pointer'. Now, the variable 'temp1' contains all the mice connected to the system.
FIGURE 2
We then check the list to make sure it is not empty. If it is empty, then there are no mice connected to the system.
Code:
temp1="$(xinput list | grep 'Mouse' | grep 'pointer')"
if [ ! -z "${temp1}" ]; then
mouse1=$temp1
fi
If the variable has content, then we place the value into the variable 'mouse1'.
Next, we do the same for 'Trackball' and place the value in the variable 'mouset1'.
So, now we can look into the code of separating out the information we need.
The Mouse and Trackball sections are the same, and I will only cover one section. The Trackball section just uses different variables for storing the ID numbers of the trackballs.
We take the variable 'mouse1' and place it into the variables 'm1' and 'm2'.
Next, we set the variable 'char' to an equal sign. This is the character we are searching for in the variable. The portion we are interested in is the small section 'ID=##'. It is the numbers that we want. The quickest way is to search for the equal sign.
Next, we perform a search for the equal sign and make a count of how many there are. Each equal sign found we place into the variable 'filtered_string':
Code:
filtered_string="${mouse1//[^${char}]/}"
Now, we can count the length of the variable to determine how many equal signs are in it:
Code:
count=${#filtered_string}
The result we put into the variable 'count'.
Next, we see if 'count' is greater than or equal to '1'. If the count is 1 or more, then we know there is a mouse we can use.
Now we can take the variable 'm1' and get the two characters after each equal sign and place it in the variable 'm2'. The value in 'm2' is then stripped of spaces on the left side, leaving the value placed into the variable 'temp'. Next, the spaces are stripped from the right side of the value and then the result is placed into the variable 'm1'. The result of the variable is the IDs of the mice separated by a newline.
We then check to see if the length of the value in the variable 'm1' is greater than 3. If the length is greater than 3, then there should be two IDs in the variable. We then place the value in the variable 'm1' into 'temp'.
We look at the value in 'temp' and place the first line into the variable 'm1' and the second line into 'm2'. We then add '2' to the 'dev_count' since we just added two mice.
If the length of the value in 'm1' is less than 3, this means there is only one mouse found and is currently in the variable 'm1', so we clear the variable 'm2'. We also increase the value in 'dev_count' by 1, since we added one mouse.
Check for up to 2 Trackballs
The next section is to check for trackballs. We use the same process, except that the IDs are placed into 't1' and 't2'.
The value searched for is not 'Mouse' but 'Trackball', which is in 'mouset1'.
If you are using a laptop with a touchpad, you can change 'Trackball' to 'TouchPad', if you would prefer to use it. It is possible to copy the section and after pasting it, change it to 'TouchPad' so you can check for all three types.
Determine if There are More, or Less, than 2 Pointing Devices
This is an easy section since we kept count of the devices as went into the variable 'dev_count'.
We check if the value of 'dev_count' is not '2'. If the count is not equal to two, we give an error message and exit with a specific exit code.
Find the Second Pointing Device
Now, we will assume the first pointing device we find in our list will be the first pointing device.
Let's create an example. We say that we have two mice hooked to the PC and that the variables 'm1' and 'm2' contain the IDs of the mice. We can assume that 'm1' is the mouse we will use as the main mouse and 'm2' is the secondary mouse. This is the same, however we find the IDs.
So, we look through each variable in the order of 'm1', 'm2', 't1' and 't2'. If the variable has a value in it, a length greater than zero, we set the variable 'found' equal to 1. If we find a variable that has a length greater than 0 and the variable 'found' is set to '1'. we can use that ID as our second mouse. The ID is then placed into the variable 'use'.
Once we have the variable in 'use', we can then run the last command that will set it up as its own cursor.
Initialize Second Mouse as a Separate Cursor
This section is the whole reason for the script. These last two commands create a second mouse pointer and then connect the second mouse to the new secondary mouse cursor.
The first thing to do is create the secondary cursor with the command:
Code:
xinput create-master "SecondaryCursor"
Now that we have a cursor set, we can attach a mouse to the cursor with ID we found with the command:
Code:
xinput reattach "$use" "SecondCursor pointer"
The mouse should now move a second cursor that is on the screen. The new cursor is independent of the original cursor controlled by the other mouse.
Keep in mind that you can also attach a keyboard as well. If you have a keyboard and mouse connected to one USB dongle, you will want to make sure you use the ID for the set when connecting them if you want to set up both. The command for a keyboard is:
Code:
xinput reattach <ID> "SecondCursor keyboard"
Again, make sure you use the correct ID.
If you want to stop the mapping, look at the 'xinput list' and find the line listed as 'SecondaryCursor' to get the ID. Use the ID in the command:
Code:
xinput --remove-master <ID>
The mapping should be stopped if you remove the mouse/keyboard or definitely will be if you reboot the system.
NOTE: The complete script is attached. It is named 'mouse.txt'. Simply rename it to 'mouse.sh' and then make it executable.
Conclusion
This can be handy if you need a system with multiple mice and keyboards. If you add the 'xinput' commands to the '.xinitrc' file, the system should keep the mapping persistent.
This may be one of those things you need to try to see how it works, and I'm sure you can come up with your own uses for this command.

