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)}