Skip to content

Instantly share code, notes, and snippets.

@btumbleson
Created May 2, 2022 21:44
Show Gist options
  • Save btumbleson/9aff2079f364c95305c63a996e0eef6d to your computer and use it in GitHub Desktop.
Save btumbleson/9aff2079f364c95305c63a996e0eef6d to your computer and use it in GitHub Desktop.
SuzoHapp Trackball Fix for Linux
# SuzoHapp Trackball Fix
# v0.1 - 11/09/2021
# https://bentumbleson.com
#
# Requires python-evdev: https://python-evdev.readthedocs.io/en/latest/
#
# This script will constantly read the input from your SuzoHapp trackball
# and automatically inject a theoretical large relative movement into to system when
# a large rotation (determined by acceleration) of the trackball is detected.
#
# This works because when the analog trackball spins too quickly, the sensor
# will either read "0" because it's moving too fast, nothing at all, or a very
# low number which is not representative of the actual speed of the trackball.
#
# In MAME games like Golden Tee (gtfore06), we have to inject movement before
# any of the 0's or small numbers can register, or else your swing in the game in
# the game will sputter, leading to undesired results. This is why the script doesn't
# listen for unexpected "drops" in relative movement, and rather needs to proactively
# attempt to determine when a drop is likely to happen so it can correct beforehand.
#
# Thus, an arbitary measure of acceleration is calculated between each instance of
# a relative movement by the trackball, and if it is less than -100k, then a relative
# action is pushed to the system ten times, each 0.08 seconds after another.
from evdev import InputDevice, InputEvent, list_devices, ecodes as e
import datetime
import time
devices = [InputDevice(path) for path in list_devices()]
for device in devices:
if device.name == "HID 1241:1111": # this is the name of your SuzoHapp Trackball - replace as needed!
# set dev equal to the matched device
dev = InputDevice(device.path)
# display the device name to start
print(dev)
# this method is used to send a simulated value to the computer
def pushValue(value):
repeatCount = 10 # replace with whatever you need, I found 10 was needed in many cases
for _ in range(repeatCount):
# pass the value as a relative Y movement
dev.write(e.EV_REL, e.REL_Y, value)
# sync
dev.write(e.EV_SYN, 0, 0)
# sleep equivilant to 120hz
time.sleep(0.008)
# we need to build up the current time so we can set define `last` before the loop starts for the first time
# this is messy and there's probably a better way to do this?
dt = datetime.datetime.now().timestamp()
sec = int(dt)
usec = int(str(dt - int(dt)).split(".")[1])
last = InputEvent(sec, usec, e.EV_REL, e.REL_Y, 0)
# process each movement of the trackball
for event in dev.read_loop():
# 2 = Relative Movement, we only want these
if event.type == 2:
# event.code of 1 = REL_Y (relative Y, since we aren't interested in x movements)
if event.code == 1:
# calculate the different of relative movement values between the last event of this type
diffValue = abs(event.value - (last.value or 0))
# calculate acceleration
diffInTimestampsSquared = (event.timestamp() - last.timestamp()) ** 2
if diffInTimestampsSquared == 0:
accel = 0
else:
accel = (event.value - last.value) / (diffInTimestampsSquared)
# uncomment to test acceleration calculation based on values
print(accel, " ---- ", event.value, last.value, (event.timestamp() - last.timestamp()))
# negative acceleration is forward movement, 100k
if accel < -100000:
pushValue(-35) # -35 is the equivialant of smacking the trackball quite hard
# set the current event to the last so we can compare to it on the next event
last = event
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment