diff --git a/src/RageInput.cpp b/src/RageInput.cpp index 05d4d6e221..9eaa949ff6 100644 --- a/src/RageInput.cpp +++ b/src/RageInput.cpp @@ -6,6 +6,11 @@ #include "LuaManager.h" #include "LocalizedString.h" +#if LINUX +// FIXME: bit gross to include this here, see GH issue #73 +#include "arch/InputHandler/LinuxInputManager.h" +#endif + #include @@ -60,6 +65,11 @@ void RageInput::LoadDrivers() m_InputHandlers.clear(); g_mapDeviceToHandler.clear(); +#if LINUX + // Recreating this forces it to re-scan for devices. + LINUXINPUT = new LinuxInputManager; +#endif + // Init optional devices. std::vector apDevices; diff --git a/src/arch/InputHandler/InputHandler_Linux_Event.cpp b/src/arch/InputHandler/InputHandler_Linux_Event.cpp index 356bce5310..1f1607b817 100644 --- a/src/arch/InputHandler/InputHandler_Linux_Event.cpp +++ b/src/arch/InputHandler/InputHandler_Linux_Event.cpp @@ -20,6 +20,7 @@ #include #include #include +#include REGISTER_INPUT_HANDLER_CLASS2( LinuxEvent, Linux_Event ); @@ -266,15 +267,33 @@ EventDevice::~EventDevice() } InputHandler_Linux_Event::InputHandler_Linux_Event() - : m_NextDevice(DEVICE_JOY10) + : m_InputMutex("InputHandler_Linux") + , m_NextDevice(DEVICE_JOY10) , m_bShutdown(true) , m_bDevicesChanged(false) + , m_udev_fd(-1) { if(LINUXINPUT == nullptr) LINUXINPUT = new LinuxInputManager; LINUXINPUT->InitDriver(this); - if( ! g_apEventDevices.empty() ) // LinuxInputManager found at least one valid device for us - StartThread(); + m_udev = udev_new(); + if( ! m_udev ) + LOG->Warn( "Failed to create udev object" ); + + m_udev_monitor = udev_monitor_new_from_netlink(m_udev, "udev"); + if( ! m_udev_monitor ) + { + LOG->Warn( "Failed to create udev object" ); + udev_unref(m_udev); + } + + udev_monitor_filter_add_match_subsystem_devtype(m_udev_monitor, "input", NULL); + udev_monitor_enable_receiving(m_udev_monitor); + + m_udev_fd = udev_monitor_get_fd(m_udev_monitor); + + // Always start thread, if only to monitor udev events + StartThread(); } InputHandler_Linux_Event::~InputHandler_Linux_Event() @@ -284,6 +303,9 @@ InputHandler_Linux_Event::~InputHandler_Linux_Event() for( int i = 0; i < (int) g_apEventDevices.size(); ++i ) delete g_apEventDevices[i]; g_apEventDevices.clear(); + + udev_monitor_unref(m_udev_monitor); + udev_unref(m_udev); } void InputHandler_Linux_Event::StartThread() @@ -315,7 +337,11 @@ bool InputHandler_Linux_Event::TryDevice(RString devfile) if( hotplug ) StartThread(); m_NextDevice = enum_add2(m_NextDevice, 1); + + m_InputMutex.Lock(); m_bDevicesChanged = true; + m_InputMutex.Unlock(); + return true; } else @@ -343,6 +369,12 @@ void InputHandler_Linux_Event::InputThread() FD_ZERO( &fdset ); int iMaxFD = -1; + if( m_udev_fd >= 0 ) + { + FD_SET( m_udev_fd, &fdset ); + iMaxFD = std::max( iMaxFD, m_udev_fd ); + } + for( int i = 0; i < (int) g_apEventDevices.size(); ++i ) { int iFD = g_apEventDevices[i]->m_iFD; @@ -361,6 +393,31 @@ void InputHandler_Linux_Event::InputThread() continue; RageTimer now; + if( FD_ISSET(m_udev_fd, &fdset) ) + { + struct udev_device* device = udev_monitor_receive_device(m_udev_monitor); + if( device ) + { + const char *action = udev_device_get_action(device); + const char *devnode = udev_device_get_devnode(device); + + if( action && devnode && strcmp(action, "add") == 0 ) + { + // Check if the device is a joystick + const char *devtype = udev_device_get_property_value(device, "ID_INPUT_JOYSTICK"); + if( devtype && strcmp(devtype, "1") == 0 ) + { + m_InputMutex.Lock(); + m_bDevicesChanged = true; + m_InputMutex.Unlock(); + LOG->Info("LinuxEvent: New joystick detected: %s\n", devnode); + } + } + + udev_device_unref(device); + } + } + for( int i = 0; i < (int) g_apEventDevices.size(); ++i ) { if( !g_apEventDevices[i]->IsOpen() ) @@ -375,6 +432,9 @@ void InputHandler_Linux_Event::InputThread() { LOG->Warn( "Error reading from %s: %s; disabled", g_apEventDevices[i]->m_sPath.c_str(), strerror(errno) ); g_apEventDevices[i]->Close(); + m_InputMutex.Lock(); + m_bDevicesChanged = true; + m_InputMutex.Unlock(); continue; } @@ -382,6 +442,9 @@ void InputHandler_Linux_Event::InputThread() { LOG->Warn("Unexpected packet (size %i != %i) from joystick %i; disabled", ret, (int)sizeof(event), i); g_apEventDevices[i]->Close(); + m_InputMutex.Lock(); + m_bDevicesChanged = true; + m_InputMutex.Unlock(); continue; } @@ -441,7 +504,9 @@ void InputHandler_Linux_Event::GetDevicesAndDescriptions( std::vectorm_Dev, pDev->m_sName) ); } + m_InputMutex.Lock(); m_bDevicesChanged = false; + m_InputMutex.Unlock(); } /* diff --git a/src/arch/InputHandler/InputHandler_Linux_Event.h b/src/arch/InputHandler/InputHandler_Linux_Event.h index b73d32ff9b..0baf0e8af1 100644 --- a/src/arch/InputHandler/InputHandler_Linux_Event.h +++ b/src/arch/InputHandler/InputHandler_Linux_Event.h @@ -25,8 +25,14 @@ private: void InputThread(); RageThread m_InputThread; + // Currently only m_bDevicesChanged is guarded by this mutex + RageMutex m_InputMutex; InputDevice m_NextDevice; bool m_bShutdown, m_bDevicesChanged; + // Used to monitor for new devices + int m_udev_fd; + struct udev* m_udev; + struct udev_monitor* m_udev_monitor; }; #define USE_INPUT_HANDLER_LINUX_JOYSTICK