Skip to content

Instantly share code, notes, and snippets.

@PeterCxy
Last active May 14, 2022 10:32
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PeterCxy/b4e256b6b4a133c93c012b9738c557ca to your computer and use it in GitHub Desktop.
Save PeterCxy/b4e256b6b4a133c93c012b9738c557ca to your computer and use it in GitHub Desktop.
Evdev long click to right click emulation

A Python script to implement long-click-to-right-click emulation on Linux. Root permission needed (or you need udev rules to allow your user to write / read from evdev)

Usage:

python rce.py <name_of_your_tuchscreen>

Use xinput output to determine the name of your touchscreen

Environment variable RCE_SCALE is used to set the scale factor of your touchscreen. (your touchscreen resolution can be significantly higher than your actual screen resolution, and your screen may also have high DPI). This will enlarge the allowed error threshold (when long-pressing you can't avoid some slight movement).

On surface pro 6 I set RCE_SCALE=6.

Note: This evdev is different from the Xorg notion of the evdev driver. You are perfectly fine if your Xorg configuration uses libinput.

#!/usr/bin/env python
import asyncio
from evdev import InputDevice, UInput, KeyEvent, ecodes
import evdev
import os
import sys
import subprocess
import time
async def main():
for path in evdev.list_devices():
dev = InputDevice(path)
if dev.name == sys.argv[1]:
await simulate_right_click(dev)
return
# Device not found
sys.stderr.write("Failed to detect the specified device.\n")
exit(1)
async def simulate_right_click(dev):
scale_factor = int(os.environ['RCE_SCALE'])
threshold = 80 * scale_factor
min_interval = 0.5
pos_x = -1
pos_y = -1
pos_last_x = -1
pos_last_y = -1
idev_caps = {
ecodes.EV_REL: (ecodes.REL_X, ecodes.REL_Y),
ecodes.EV_KEY: (ecodes.BTN_RIGHT, ecodes.BTN_TOUCH)
}
idev = UInput(idev_caps)
trigger_task = None
# The task function used to trigger right clicks
async def trigger_right_click():
await asyncio.sleep(min_interval)
dX = abs(pos_last_x - pos_x)
dY = abs(pos_last_y - pos_y)
if dX <= threshold and dY <= threshold:
idev.write(ecodes.EV_REL, ecodes.REL_X, 1)
idev.write(ecodes.EV_REL, ecodes.REL_Y, 1)
idev.write(ecodes.EV_KEY, ecodes.BTN_TOUCH, 0)
idev.syn()
idev.write(ecodes.EV_KEY, ecodes.BTN_RIGHT, 1)
idev.syn()
idev.write(ecodes.EV_KEY, ecodes.BTN_RIGHT, 0)
idev.syn()
trigger_task = None
async for ev in dev.async_read_loop():
if ev.type == ecodes.EV_ABS:
abs_type = ecodes.ABS[ev.code]
# Track the position of touch
# Note that this position is not 1:1 to the screen resolution
if abs_type == "ABS_X" or abs_type == "ABS_MT_POSITION_X":
pos_x = ev.value
elif abs_type == "ABS_Y" or abs_type == "ABS_MT_POSITION_Y":
pos_y = ev.value
elif ev.type == ecodes.EV_KEY:
tev = KeyEvent(ev)
if tev.keycode == 'BTN_TOUCH':
if tev.keystate == KeyEvent.key_down:
if trigger_task != None:
trigger_task.cancel()
trigger_task = asyncio.get_event_loop().create_task(trigger_right_click())
pos_last_x = pos_x
pos_last_y = pos_y
elif tev.keystate == KeyEvent.key_up:
if trigger_task != None:
trigger_task.cancel()
idev.close()
asyncio.get_event_loop().run_until_complete(main())
# vim: tabstop=2 softtabstop=2 shiftwidth=2 expandtab
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment