From f0394b721ed85d203db865e71eb8d6f1f37c913b Mon Sep 17 00:00:00 2001 From: din Date: Wed, 21 Aug 2024 20:45:38 -0500 Subject: [PATCH] sort by location --- src/arch/InputHandler/LinuxInputManager.cpp | 104 ++++++++++++++++---- src/arch/InputHandler/LinuxInputManager.h | 20 +++- 2 files changed, 101 insertions(+), 23 deletions(-) diff --git a/src/arch/InputHandler/LinuxInputManager.cpp b/src/arch/InputHandler/LinuxInputManager.cpp index 2655dfe7fb..cd69146fb2 100644 --- a/src/arch/InputHandler/LinuxInputManager.cpp +++ b/src/arch/InputHandler/LinuxInputManager.cpp @@ -13,15 +13,23 @@ #include #endif +#if defined(HAVE_FCNTL_H) +#include +#endif + +#include + #include +Preference g_bInputLinuxOrderByLocation( "InputLinuxOrderByLocation", false ); + RString getDevice(RString inputDir, RString type) { RString result = ""; DIR* dir = opendir( inputDir.c_str() ); if(dir == nullptr) { LOG->Warn("LinuxInputManager: Couldn't open %s: %s.", inputDir.c_str(), strerror(errno) ); return ""; } - + struct dirent* d; while( ( d = readdir(dir) ) != nullptr) if( strncmp( type.c_str(), d->d_name, type.size() ) == 0) @@ -29,7 +37,7 @@ RString getDevice(RString inputDir, RString type) result = RString("/dev/input/") + d->d_name; break; } - + closedir(dir); return result; } @@ -46,10 +54,10 @@ LinuxInputManager::LinuxInputManager() // HACK: If empty, assume both are enabled if( g_sInputDrivers.Get() == "" ) { m_bEventEnabled = true; m_bJoystickEnabled = true; } - + m_EventDriver = nullptr; m_JoystickDriver = nullptr; - + // XXX: Can I use RageFile for this? DIR* sysClassInput = opendir("/sys/class/input"); if( sysClassInput == nullptr) @@ -58,33 +66,93 @@ LinuxInputManager::LinuxInputManager() LOG->Warn("Couldn't open /sys/class/input: %s. Joysticks will not work!", strerror(errno) ); return; } - + struct dirent* d; while( ( d = readdir(sysClassInput) ) != nullptr) { if( strncmp( "input", d->d_name, 5) != 0) continue; - + RString dName = RString("/sys/class/input/") + d->d_name; - + bool bEventPresent = getDevice(dName, "event") != ""; - if( m_bEventEnabled && bEventPresent ) + if( m_bEventEnabled && bEventPresent ) { m_vsPendingEventDevices.push_back(dName); continue; } - + bool bJoystickPresent = getDevice(dName, "js") != ""; if( m_bJoystickEnabled && bJoystickPresent ) { m_vsPendingJoystickDevices.push_back(dName); continue; } - + if( !bEventPresent && !bJoystickPresent ) LOG->Info("LinuxInputManager: %s seems to have no eventNN or jsNN.", dName.c_str() ); } - // Sort devices for more consistent numbering. - std::sort(m_vsPendingEventDevices.begin(), m_vsPendingEventDevices.end(), cmpDevices); - std::sort(m_vsPendingJoystickDevices.begin(), m_vsPendingJoystickDevices.end(), cmpDevices); + if(g_bInputLinuxOrderByLocation) + { + // use Presort to sort the devices by unique location (like USB port/hub number.) + PresortPhysical(m_vsPendingEventDevices, "event"); + PresortPhysical(m_vsPendingJoystickDevices, "js"); + } + else + { + // Sort devices for more consistent numbering. + std::sort(m_vsPendingEventDevices.begin(), m_vsPendingEventDevices.end(), cmpDevices); + std::sort(m_vsPendingJoystickDevices.begin(), m_vsPendingJoystickDevices.end(), cmpDevices); + } closedir(sysClassInput); } + +static bool presort_cmpDevices(LinuxInputSort a, LinuxInputSort b) +{ + return a.UniqueString < b.UniqueString; +} + +void LinuxInputManager::PresortPhysical(std::vector& sortingArray, RString sortBy) +{ + m_vPreSort.clear(); + + for (RString &dev : sortingArray) + { + LinuxInputSort entry; + + entry.DeviceName = dev; + entry.UniqueString = ""; + + int m_iFD = open(getDevice(dev, sortBy.c_str()).c_str(), O_RDWR); + + if(m_iFD >= 0) + { + char szLocation[1024]; + + // EVIOCGPHYS can return values like "usb-0000:0d:00.3-4" + // telling us where the device is located physically on the computer. + if(ioctl(m_iFD, EVIOCGPHYS(sizeof(szLocation)), szLocation) != -1) + { + entry.UniqueString += szLocation; + } + + close(m_iFD); + } + + // in the event we didn't get a unique location from ioctl + // default to the name so that sorting by number still works as intended. + entry.UniqueString += entry.DeviceName; + + m_vPreSort.push_back(entry); + } + + std::sort(m_vPreSort.begin(), m_vPreSort.end(), presort_cmpDevices); + + sortingArray.clear(); + + for (LinuxInputSort &dev : m_vPreSort) + { + // LOG->Info("%s -> %s", dev.DeviceName.c_str(), dev.UniqueString.c_str()); + sortingArray.push_back(dev.DeviceName); + } +} + void LinuxInputManager::InitDriver(InputHandler_Linux_Event* driver) { m_EventDriver = driver; @@ -93,7 +161,7 @@ void LinuxInputManager::InitDriver(InputHandler_Linux_Event* driver) { RString devFile = getDevice(dev, "event"); ASSERT( devFile != "" ); - + if( ! driver->TryDevice(devFile) && m_bJoystickEnabled && getDevice(dev, "js") != "" ) m_vsPendingJoystickDevices.push_back(dev); } @@ -105,7 +173,7 @@ void LinuxInputManager::InitDriver(InputHandler_Linux_Event* driver) void LinuxInputManager::InitDriver(InputHandler_Linux_Joystick* driver) { m_JoystickDriver = driver; - // Discard all the joystick devices if they were assigned manually via + // Discard all the joystick devices if they were assigned manually via // InputDeviceOrder if( g_sInputDeviceOrder.Get() != "" ) { m_vsPendingJoystickDevices.clear(); @@ -115,7 +183,7 @@ void LinuxInputManager::InitDriver(InputHandler_Linux_Joystick* driver) { RString devFile = getDevice(dev, "js"); ASSERT( devFile != "" ); - + driver->TryDevice(devFile); } @@ -134,7 +202,7 @@ LinuxInputManager* LINUXINPUT = nullptr; // global and accessible anywhere in ou /* * (c) 2013 Ben "root" Anderson * All rights reserved. - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -144,7 +212,7 @@ LinuxInputManager* LINUXINPUT = nullptr; // global and accessible anywhere in ou * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF diff --git a/src/arch/InputHandler/LinuxInputManager.h b/src/arch/InputHandler/LinuxInputManager.h index c773b3f372..9221a18aab 100644 --- a/src/arch/InputHandler/LinuxInputManager.h +++ b/src/arch/InputHandler/LinuxInputManager.h @@ -7,9 +7,16 @@ class InputHandler_Linux_Joystick; class InputHandler_Linux_Event; -// Enumerates the input devices on the system and dispatches them to +// Enumerates the input devices on the system and dispatches them to // IH_Linux_Event and IH_Linux_Joystick as appropriate. +// Helper struct for keeping track of inputs. +struct LinuxInputSort +{ + RString DeviceName; + RString UniqueString; +}; + class LinuxInputManager { public: @@ -18,10 +25,13 @@ public: void InitDriver(InputHandler_Linux_Event* drv); ~LinuxInputManager(); private: + std::vector m_vPreSort; + void PresortPhysical(std::vector& sortingArray, RString sortBy); + bool m_bEventEnabled; InputHandler_Linux_Event* m_EventDriver; std::vector m_vsPendingEventDevices; - + bool m_bJoystickEnabled; InputHandler_Linux_Joystick* m_JoystickDriver; std::vector m_vsPendingJoystickDevices; @@ -34,7 +44,7 @@ extern LinuxInputManager* LINUXINPUT; // global and accessible from anywhere in /* * (c) 2013 Ben "root" Anderson * All rights reserved. - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -44,7 +54,7 @@ extern LinuxInputManager* LINUXINPUT; // global and accessible from anywhere in * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF @@ -54,4 +64,4 @@ extern LinuxInputManager* LINUXINPUT; // global and accessible from anywhere in * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. - */ \ No newline at end of file + */