InfinityDevil

How to Disable a Touchscreen on Ubuntu Linux

"HP Laptop with touchscreen"

I have an old HP laptop with a touchscreen that keeps registering phantom taps, so I can't use it with Ubuntu Linux unless I find a way to suppress the touchscreen input. This was implemented on Ubuntu 22.10. I'm sure more skilled shell script authors can do this more easily than I did with Python below, but this has worked for me and perhaps it will help you.

The Strategy

The basics are to (a) reliably find the device at boot time, then (b) capture all of its input so it doesn't affect the system. I tried several other tutorials to try and turn off the device at boot time but I was not able to get that strategy to work.

Packages to Install

First up we need two new packages -- libinput-tools and evtest -- as well as Python 3.

sudo apt install libinput-tools
sudo apt install evtest

The Commands

This command will show us the devices. One of these will be the touchscreen:

sudo libinput list-devices

Devices are shown in blocks and there are two important fields. The Device and the Kernel. I've read that the event number attached to the device can change at boot time.

Here's an example of just two entries in the output of that libinput command:

Device:           eGalax Inc. eGalaxTouch EXC7910-1023-12.00.00
Kernel:           /dev/input/event5
Group:            7
Seat:             seat0, default
Size:             341x195mm
Capabilities:     touch 
Tap-to-click:     n/a
Tap-and-drag:     n/a
Tap drag lock:    n/a
Left-handed:      n/a
Nat.scrolling:    n/a
Middle emulation: n/a
Calibration:      identity matrix
Scroll methods:   none
Click methods:    none
Disable-w-typing: n/a
Disable-w-trackpointing: n/a
Accel profiles:   n/a
Rotation:         n/a

Device:           HP Truevision HD: HP Truevision
Kernel:           /dev/input/event17
Group:            8
Seat:             seat0, default
Capabilities:     keyboard 
Tap-to-click:     n/a
Tap-and-drag:     n/a
Tap drag lock:    n/a
Left-handed:      n/a
Nat.scrolling:    n/a
Middle emulation: n/a
Calibration:      n/a
Scroll methods:   none
Click methods:    none
Disable-w-typing: n/a
Disable-w-trackpointing: n/a
Accel profiles:   n/a
Rotation:         n/a

The Capabilities: touch line is the key. I want the "eGalax" device, and the Kernel event is what we need:

Device: eGalax Inc. eGalaxTouch EXC7910-1023-12.00.00
Kernel: /dev/input/event5

To see if this is the right device, we use this evtest command to capture the input from that device and send it to null to prevent it from registering touches to the screen:

sudo evtest --grab /dev/input/event5 > /dev/null

We will create a new service to be run by systemd at boot time to do this job for us.

The systemd Game Plan

  1. Create a .sh file to be run by the service (see file below).
  2. Copy the script to /usr/local/bin/
  3. Make it executable (sudo chmod +x /usr/local/bin/scriptname.sh)
  4. Copy the event_of_device.py to /root/ (sudo cp event_of_device.py /root/)
  5. Create a service unit file (see file below).
  6. Find a name not taken. View results at: sudo systemctl list-unit-files --type=service
  7. Place the .service file in /etc/systemd/system/ folder.
  8. Set permissions on the .service file: sudo chmod 640 /etc/systemd/system/filename.service
  9. Check the syntax: sudo systemctl status filename.service
  10. Reload the unit file definitions: sudo systemctl daemon-reload
  11. Set the service to start at boot time: sudo systemctl enable servicename
  12. Start the service: sudo systemctl start servicename
  13. Verify it is running: sudo systemctl status servicename.service
  14. To stop the service, sudo systemctl stop servicename.service
  15. To keep it from being launched at startup: sudo systemctl disable servicename.service

The Files for Steps 1, 4 and 5 are below.

1. Create a .sh file to be run by the service.

Here's the shell script file. It runs the python program the first time to drop the information into a txt file so we can see what happened at runtime, then runs it again to store the value. You'll replace the eGalax with the substring matching your particular touchscreen device.

touchscreen_disable.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/bash

# dump the set of current devices to be parsed for the touchscreen event
sudo /usr/bin/libinput list-devices > /root/libinput_list_devices.txt

# parse it and write the event into a file.
sudo /usr/bin/python3 /root/event_of_device.py eGalax /root/libinput_list_devices.txt > /root/touchscreen_device.txt

# parse it and store it in the sDevice variable
sDevice=$( sudo /usr/bin/python3 /root/event_of_device.py eGalax /root/libinput_list_devices.txt )

# write the variable to a file.
echo $sDevice > /root/sDevice.txt

# if the variable is not empty we execute the evtest command to constrain the device.
if [ -n "$sDevice" ]
then $( sudo /usr/bin/evtest --grab "$sDevice" > /dev/null ) &
fi

4. Copy the event_of_device.py to /root/ (sudo cp event_of_device.py /root/)

I created a python program to parse the libinput output and find a particular device by substring of the name. I'm sure experienced Python coders could do a much better job but it works. It's called event_of_device.py

import sys
from os.path import exists
import argparse

# event_of_device.py
#
# Script to parse the text output of sudo libinput list-devices, find a given device, and output
# the part after a colon in the first "kernel" line after the device name is found.
# Written because I have no idea how to use awk to do this, and because I know a little bit about
# python.  Oh, really why was it written?  Oh because I needed to find a touchscreen device, and
# tell evtest to grab all of its events and send it to null to keep an old laptop from randomly
# giving touches on screen when running Ubuntu 22.10 using the command 
# "sudo evtest --grab /dev/input/event5 > /dev/null"
# Where the /dev/input/event5 is an example of what this script will output.
#
# 2023-02-14 by REDACTED
# 2023-02-25 updated to use argparse library to parse arguments.

# variables
sDevice = ""
sInputFile = ""
oFile = None
listIn = None
sLine = ""
nLine = -1
nFound = -1
nDeviceLine = -1
nKernelLine = -1
sEvent = ""
sOut = ""
listError = []
sUsage = ""
sVersion = "2023-02-25.21"

# Debugging flag
bDebug = False

# maximum rows allowed in input file, 12k sounds good
nBytesCap = (12*1024)

# Python doesn't have a null, but the None can be tested using "is" not "==".

# parse the command line arguments
parser = argparse.ArgumentParser(
    description='Searches the input file for the block containing the device value, returns the Kernel value from that block.',
    epilog='Version '+sVersion
)
parser.add_argument("-d", "--device", required=True, type=str, 
                    help="device string to search for")
parser.add_argument("-i", "--inputfile", required=True, type=str, 
                    help="file containing the output of 'sudo libinput list-devices'")
args = parser.parse_args()

# fetch parameter values
sDevice = args.device
sInputFile = args.inputfile
if(bDebug):
    print("Device: " + sDevice)
    print("Input File: " + sInputFile)

# does the file with that name exist?
if(not(exists(sInputFile))):
    listError.append("InputFilename file \""+sInputFile+"\" not found.")

# show the error if we have one
if(len(listError) > 0):
    sError = ""
    for sLine in listError:
        sError = sError + sLine + "\n"

    parser.print_help()

    raise ValueError(sError)

# open file is read, text modes
oFile = open(sInputFile, "rt")

# grab the first set of bytes from the file
listIn = oFile.readlines(nBytesCap)

# Each device in the file is separated by a single 
# \n new line character blank line.

# find the first row in the list with a 
# substring matching the device
# if none is found, throw an error and exit
nFound = -1
nDeviceLine = -1
nLine = 0
while (nLine < len(listIn)) and (nFound == -1):
    sLine = listIn[nLine]
    nFound = sLine.find(sDevice)
    if nFound > -1:
        nDeviceLine = nLine
    if(bDebug):
        print("Line \"" + sLine + "\" nFound = "+str(nFound))
    nLine = nLine + 1

if(bDebug):
    print("Device found value: " + str(nFound))
    print("Device on line " + str(nDeviceLine))
    print("Lines searched: "+str(nLine))

# find the Kernel line after the device line.
if nDeviceLine > -1:
    nFound = -1
    nLine = nDeviceLine + 1
    while (nLine < len(listIn) and (nFound == -1)):
        sLine = listIn[nLine]
        nFound = sLine.find("Kernel:")
        if nFound > -1:
            nDeviceLine = nLine
        nLine = nLine + 1
    # did we find it?
    if nDeviceLine > -1:
        # take the event value after the tab.
        listEvent = listIn[nDeviceLine].split(":")
        if len(listEvent) > 0:
            sEvent = listEvent[1].strip()

if(bDebug):
    print("Event value: " + sEvent)

# send out the event
sOut = sEvent

# close the file.
oFile.close()

# show the output value
print(sOut)

5. Create a service unit file

This file is named touchscreen-disable.service

[Unit]
Description=Captures all touchscreen events to effectively disable it.
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/touchscreen-disable.sh
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=default.target

Like I said, I'm sure there are more elegant ways to do it, but this is how I got it to work. I hope it helps you.

Source: How to run a Linux Program at Startup with systemd