A workaround for the Northwest drift issue
-
Re: [SOLVED] Selection jumps after the first input of a controller
This issue has been pestering me for a while now. It causes the selection in Emulation Station to "jump" after the first input of a controller.. It causes the selection in my game to endlessly "scroll" or "stick" when other controllers (excluding player 1) are connected.. All because Linux initializes the axes of every controller's analog stick to 0 instead of its center position.
Only after I press something on all connected controllers do the axis positions update.. and then I'm freed from Northwest drift. What does this mean for my wireless USB controllers that aren't switched on or their batteries removed? Too bad; I must either remove the wireless dongle from the USB port, or switch on the controller & press something. Every time I (re)boot the Pi, this is what it takes. Such a nuisance.
But hey, maybe you're reading this & thinking "..I never had this issue." Well, that's because your connected controllers have analog axis ranges that are zero-centered. For example, controllers with Xbox (X) input have axis ranges from -32767 to 32768. But for controllers with Direct (D) input, their axis ranges are 0 to 255.. a center of 128. Guess which ones I have. And what position is 0 on the horizontal & vertical axes of those analog sticks? Northwest.
Now maybe you were once like me & avoided the issue by rebuilding the kernel with Raphnet's fixes. That's great. As of now though, those fixes aren't updated. I know because they failed when I tried to compile them.. and I dare not experiment with kernels. What I will do is create a far-less strenuous workaround..
TL;DR
Forget my first attempt at this, which only helped prevent the jumps in Emulation Station. This workaround does the trick system-wide:
- Update your repositories.
sudo apt update
- Install python3.
sudo apt install python3
- Install python3-pip.
sudo apt install python3-pip
- Install python3's evdev module (must be via sudo for the udev rule in Steps 7 thru 9 to work).
sudo pip3 install evdev
- After connecting your controllers, run the command below & make note of the following for each one:
a. the exact name
b. the event handler #
cat /proc/bus/input/devices
- Now run the command below & make note of the following for every analog stick axis of each controller:
a. the event code (axes are prefixed with "ABS_")
b. the midpoint (calculate via (Max - Min) / 2 + Min, rounded to nearest integer)
IGNORE ANY AXIS WHERE ITS MINIMUM & MAXIMUM NEGATE EACH OTHER; ITS MIDPOINT IS 0 AND THE AXIS SHOULD BE LEFT ALONE BY THIS WORKAROUND. In the command below, substitute the event# with that of the associated controller. Then Ctrl-C to exit.
evtest /dev/input/event#
- Create a new udev rule. You may change the name, but leave the numerical prefix & file extension as-is.
sudo nano /etc/udev/rules.d/99-joystick.rules
- Add the following code line in the rules file per controller using the name, event codes, & midpoints you gathered earlier. You will need to edit the code line at the [BRACKETED ENTRIES]. Notice that for each d.write command, the last 2 arguments contain the event code & midpoint. In my controller, I had four analog axes.. so add/remove axes (aka the d.write commands) as needed for your controller.
SUBSYSTEM=="input", KERNEL=="event*", ACTION=="add", ATTRS{name}=="[EXACT CONTROLLER NAME]", RUN+="/usr/bin/python3 -c \"import evdev as v; from evdev import ecodes as e; d = v.InputDevice('%E{DEVNAME}'); d.write(e.EV_ABS, e.[1st AXIS EVENT CODE], [1st AXIS MIDPOINT]); d.write(e.EV_ABS, e.[2nd AXIS EVENT CODE], [2nd AXIS MIDPOINT]); d.write(e.EV_ABS, e.[3rd AXIS EVENT CODE], [3rd AXIS MIDPOINT]); d.write(e.EV_ABS, e.[4th AXIS EVENT CODE], [4th AXIS MIDPOINT]); d.write(e.EV_SYN, 0, 0); d.close()\""
- If you haven't already, follow the previous step for your remaining controllers as additional code lines. Then Ctrl-X to exit, Y to save, Enter to confirm. Here is my finished udev rule file for comparison:
SUBSYSTEM=="input", KERNEL=="event*", ACTION=="add", ATTRS{name}=="Logitech Logitech Cordless RumblePad 2", RUN+="/usr/bin/python3 -c \"import evdev as v; from evdev import ecodes as e; d = v.InputDevice('%E{DEVNAME}'); d.write(e.EV_ABS, e.ABS_X, 128); d.write(e.EV_ABS, e.ABS_Y, 128); d.write(e.EV_ABS, e.ABS_Z, 128); d.write(e.EV_ABS, e.ABS_RZ, 128); d.write(e.EV_SYN, 0, 0); d.close()\"" SUBSYSTEM=="input", KERNEL=="event*", ACTION=="add", ATTRS{name}=="8Bitdo SF30 Pro", RUN+="/usr/bin/python3 -c \"import evdev as v; from evdev import ecodes as e; d = v.InputDevice('%E{DEVNAME}'); d.write(e.EV_ABS, e.ABS_X, 128); d.write(e.EV_ABS, e.ABS_Y, 128); d.write(e.EV_ABS, e.ABS_Z, 128); d.write(e.EV_ABS, e.ABS_RZ, 128); d.write(e.EV_SYN, 0, 0); d.close()\"" SUBSYSTEM=="input", KERNEL=="event*", ACTION=="add", ATTRS{name}=="8Bitdo SN30 Pro", RUN+="/usr/bin/python3 -c \"import evdev as v; from evdev import ecodes as e; d = v.InputDevice('%E{DEVNAME}'); d.write(e.EV_ABS, e.ABS_X, 128); d.write(e.EV_ABS, e.ABS_Y, 128); d.write(e.EV_ABS, e.ABS_Z, 128); d.write(e.EV_ABS, e.ABS_RZ, 128); d.write(e.EV_SYN, 0, 0); d.close()\""
- Reload the udev interface to take on the new rule file.
sudo udevadm control --reload
- Reconnect your controllers.. or reboot to force reconnection.
sudo reboot
This workaround will override Linux's initializations. The axes of every controller's analog stick will be set to the center position instead of 0. Bye-bye Northwest drift issue.
-
@louiehummv Hey, just wanted to say thank you for this. Been having this issue for a couple years now, I admittedly was a bit intimidated by the command line stuff here but after getting into it for a second it was a breeze, and it worked perfectly. Cheers!
-
Contributions to the project are always appreciated, so if you would like to support us with a donation you can do so here.
Hosting provided by Mythic-Beasts. See the Hosting Information page for more information.