mameau%CWD%:> █
OFFLINE
$ cat sdl-linux-joystick.txt
SDL Controllers (not just) in Linux, research 2022-04 (WIP)

WhHUHy?
===============================================================================
 MAME SDL input reconnections do/did not work on Linux resulting in the need to
 restart (not reset) a machine
 https://www.reddit.com/r/MAME/comments/u4d1xl/when_controller_disconnects_and_reconnects_it/


EVENTs
===============================================================================
SDL_JOYDEVICEADDED   outputs a device index, can be passed to SDL_JoystickOpen()
SDL_JOYDEVICEREMOVED outputs an (event or) instance id

Initalizing SDL_Init(SDL_INIT_JOYSTICK) triggers an SDL_JOYDEVICEADDED event 
for all currently connected controller

DEVICE INDEX is only (ever?) used during SDL_JoystickOpen()
INSTANCE ID is how SDL tracks controllers

SDL_JOYDEVICEADDED behaves differently when libudev is installed(?)

"As discussed (possibly to death), the Linux joystick driver does not
actually report events for added or removed joysticks when you
haven’t got udev support." - T_Joseph_Carter Oct'13

- Instance is a pointer
- Index is device index

GUIDs
===============================================================================
SDL generates a GUID which is based off of the below, it doesn't do enough to 
identify controllers.

BUS(type) 0x000, VENDOR 0x000, PRODUCT 0x000, VERSION 0x000
SERIAL is available for SDL 2.0.14+

in /sys these map to /sys/bus/hid/devices/*/input*/id/ info where 

$ ls -la /sys/bus/hid/devices/0005\:054C\:0CE6.015A/input/input888/id
total 0
drwxr-xr-x 2 root root    0 Apr 26 17:11 .
drwxr-xr-x 7 root root    0 Apr 26 17:11 ..
-r--r--r-- 1 root root 4096 Apr 26 17:11 bustype
-r--r--r-- 1 root root 4096 Apr 26 17:11 product
-r--r--r-- 1 root root 4096 Apr 26 17:11 vendor
-r--r--r-- 1 root root 4096 Apr 26 17:11 version

Looking at SDL_dinputjoystick.c and SDL_windows_gaming_input.c the approach to
generating a GUID doesn't looks to be any different and in testing SDL on Windows
through a small application seat swapping behaviour is consistent with Linux

The serial maps to /sys/bus/hid/devices/*/input*/uniq

$ cat /sys/bus/hid/devices/0005\:054C\:0CE6.015A/input/input888/uniq
d0:bc:c1:a3:76:4e

We can confirm this by looking up sdl2.SDL_JoystickGetSerial() returns the 
serial as a string
d0:bc:c1:a3:76:4e

PROBLEM: two devices on a single dongle (e.g. Steam controller) become 
interchangable due to a GUID clash(?) where player index 0 maybe become index 1 on
reconnect.

GUID generation information is available LN:230 src/joystick/linux/SDL_sysjoystick.c

GUID is byteswapped for compat on alt endian systems
Example:
03000000-4c05-0000-cc09-000000016800

0300                bus type (Padded+)  (0300 USB / 0500 Bluetooth)
0000            
4c05                Vendor ID
-
0000                
-
e60c                Product ID
-
0000
0001                Version(?) (+Padded)
6800                ???

Not sure about version, should be
$ cat /sys/bus/hid/devices/0003\:054C\:09CC.*/input/input*/id/version
8111

lsusb output
Bus 001 Device 089: ID 054c:09cc Sony Corp. DualShock 4 [CUH-ZCT2x]


Device ID in /sys/bus/hid/devices
0003:054C:09CC.019B

0003  Bus       (0003 USB / 0005 Bluetooth)
054C  Vendor
09CC  ProductId
019B  System Assigned Id(?), changes on reconnect


PRODUCT: 2508, VERSION: 256, VENDOR: 1356, TYPE: 1
PRODUCT: 0x9cc, VERSION: 0x100, VENDOR: 0x54c, TYPE: 0x1


SDL Joystick ID/Indexes
===============================================================================
Devices (/dev/input/js*) aren't tracked in SDL, instead they are using 
instances, device indexes aren't tracked after the initial SDL_JoystickOpen().

When a joystick is opened with SDL_JoystickOpen() SDL uses the instance id 
with SDL_JoystickInstanceID()

A player index is available and maps to the player seat for the application 
accessible through SDL_JoystickGetPlayerIndex

You can access data using device index with SDL_JoystickGetDevice*() functions

device index
SDL_JoystickGetDeviceGUID
SDL_JoystickGetDeviceInstanceID
SDL_JoystickGetDevicePlayerIndex
SDL_JoystickGetDeviceProduct
SDL_JoystickGetDeviceProductVersion
SDL_JoystickGetDeviceType
SDL_JoystickGetDeviceVendor


joystick instance
SDL_JoystickGetGUID
SDL_JoystickInstanceID
SDL_JoystickGetPlayerIndex
SDL_JoystickGetProduct
SDL_JoystickGetProductVersion
SDL_JoystickGetSerial
SDL_JoystickGetType
SDL_JoystickGetVendor


Reconnection/Reboots
===============================================================================
When a seat becomes free in SDL the default behaviour on SDL_JOYDEVICEADDED is 
to fill the vacant seat.

Example:
Disconnecting player index 0 and player index 2 then connecting index 2 again
will result in the device previously on player index 2 will become index 0 

it does not matter what the GUID of the device is.

PROBLEM: If two devices with the same GUID are present and reconnect at the 
same time a race condition is present for player index (unless a serial is 
available?), serial doesn't appear to impact this (without additional checks?)


Device ids changing on reconnect/reboot

Doesn't appear to be restricted to Linux MAME, just Windows doesn't use SDL as 
by default

"Not reporting a serial number doesn’t violate the standard. Different operating 
systems have different ways of generating the device ID for devices that don’t 
report a serial number. (Windows and Linux do it based on order of enumeration, 
macOS does it based on bus topology. Order of enumeration isn’t entirely 
deterministic, so you get these changing IDs on Linux. On Mac you should get 
the same ID provided you have the same arrangement of USB controllers, hubs and
 devices connected.) The GUID from SDL is generated from the device ID supplied 
 by the OS, so if the device ID isn’t stable the GUID can change. It isn’t 
 really a GUID in the normal sense of the word, it’s just the same general 
 arrangement of bytes." - cuavas Dec, 2019

Problem isn't restricted to SDL either, as per devreorder github dinput8 games
would suffer from the same problem with enumerated devices in Windows.

"The main use of this tool is to bring some sanity to older DirectInput games 
that rely on the enumeration order of devices to determine the controller order
in game (even though they are not supposed to do that). After Windows XP, and 
especially starting with Windows 8, the enumeration order of controllers is 
quite arbitrary and will change after rebooting Windows or unplugging and 
replugging in your devices. This can wreck havoc on games where you have 
carefully set up controller bindings for players 1-4, only to find that they 
are totally ruined the next time you boot up Windows.

There is no supported way to change this order, and the only method I found 
before writing devreorder was to physically unplug all of your devices and plug 
them back in the order that you want. This technique, however, falls apart when 
dealing with wireless and virtual devices. This tool finally allows the order 
to be defined explicitly." - devreorder github README.md


Linux js devices
===============================================================================
Linux will take the first available (from zero) index for creation of the js 
device on connection. 

Example:
if you have 3 controllers js0, js1, js2 and disconnect js0 then js2 and replug
js2 before js0, the previous js2 device will become js0 

This does not have a direct affect on the SDL player index until all controllers 
are disconnected and reconnected out of the original sequence, SDL will however
increment the instance id of the new controllers and populate empty seats 
(see: Reconnection)


REFERENCEs
===============================================================================
https://www.kernel.org/doc/html/latest/input/joydev/joystick-api.html
https://wiki.libsdl.org/FrontPage
https://pysdl2.readthedocs.io/en/0.9.11/
https://discourse.libsdl.org/t/sdl-added-support-for-dev-input-js-on-linux/33415
https://discourse.libsdl.org/t/joystick-api-behavior/20033/6
https://discourse.libsdl.org/t/problem-with-two-identical-joysticks/22343
https://technicallycompetent.com/joysticks-linux-joydev-evdev/
https://www.reddit.com/r/linux_gaming/comments/5xics8/a_big_problem_with_joysticks_in_linux_right_now/
https://www.reddit.com/r/MAME/comments/ehbi2m/stable_controller_ids_please_help/
https://docs.mamedev.org/advanced/devicemap.html
https://github.com/briankendall/devreorder
https://github.com/gabomdq/SDL_GameControllerDB



How windows handles devices

Find deviceIDs with wmi 
(https://devblogs.microsoft.com/powershell/displaying-usb-devices-using-wmi/)

gwmi Win32_USBControllerDevice |%{[wmi]($_.Dependent)} | Sort Manufacturer,Description,DeviceID

https://docs.microsoft.com/en-us/windows-hardware/drivers/install/device-identification-strings
https://docs.microsoft.com/en-us/windows-hardware/drivers/install/device-instance-ids
https://docs.microsoft.com/en-us/windows-hardware/drivers/install/standard-usb-identifiers
https://docs.microsoft.com/en-us/windows-hardware/drivers/install/container-ids

\?HID#VID_045E&PID_0053#7&18297dcb&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}


\?
(bus)
#
VID_(hhhh)
&
PID_(hhhh)
#
(REV_r(n)
&
(hhhhhhhh)
&
(n)
&
(nnnn)
#
{(guid)}