diff --git a/src/InputManager.cpp b/src/InputManager.cpp index 2b13fc992..74ca08efa 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -6,29 +6,11 @@ #include #include "platform.h" -#if defined(WIN32) || defined(_WIN32) - #include -#endif - namespace fs = boost::filesystem; -//----- InputDevice ---------------------------------------------------------------------------- - -InputDevice::InputDevice(const std::string & deviceName, unsigned long vendorId, unsigned long productId) - : name(deviceName), vendor(vendorId), product(productId) -{ -} - -bool InputDevice::operator==(const InputDevice & b) const -{ - return (name == b.name && vendor == b.vendor && product == b.product); -} - -//----- InputManager --------------------------------------------------------------------------- - InputManager::InputManager(Window* window) : mWindow(window), - mJoysticks(NULL), mInputConfigs(NULL), mKeyboardInputConfig(NULL), mPrevAxisValues(NULL), - mNumJoysticks(0), mNumPlayers(0), devicePollingTimer(NULL) + mKeyboardInputConfig(NULL), + mNumJoysticks(0), mNumPlayers(0) { } @@ -37,188 +19,69 @@ InputManager::~InputManager() deinit(); } -std::vector InputManager::getInputDevices() const -{ - std::vector currentDevices; - - //retrieve all input devices from system -#if defined (__APPLE__) - #error TODO: Not implemented for MacOS yet!!! -#elif defined(__linux__) - //open linux input devices file system - const std::string inputPath("/dev/input"); - fs::directory_iterator dirIt(inputPath); - while (dirIt != fs::directory_iterator()) { - //get directory entry - std::string deviceName = (*dirIt).path().string(); - //remove parent path - deviceName.erase(0, inputPath.length() + 1); - //check if it start with "js" - if (deviceName.length() >= 3 && deviceName.find("js") == 0) { - //looks like a joystick. add to devices. - currentDevices.push_back(InputDevice(deviceName, 0, 0)); - } - ++dirIt; - } - //or dump /proc/bus/input/devices anbd search for a Handler=..."js"... entry -#elif defined(WIN32) || defined(_WIN32) - RAWINPUTDEVICELIST * deviceList = nullptr; - UINT nrOfDevices = 0; - //get number of input devices - if (GetRawInputDeviceList(deviceList, &nrOfDevices, sizeof(RAWINPUTDEVICELIST)) != -1 && nrOfDevices > 0) - { - //get list of input devices - deviceList = new RAWINPUTDEVICELIST[nrOfDevices]; - if (GetRawInputDeviceList(deviceList, &nrOfDevices, sizeof(RAWINPUTDEVICELIST)) != -1) - { - //loop through input devices - for (unsigned int i = 0; i < nrOfDevices; i++) - { - //get device name - char * rawName = new char[2048]; - UINT rawNameSize = 2047; - GetRawInputDeviceInfo(deviceList[i].hDevice, RIDI_DEVICENAME, (void *)rawName, &rawNameSize); - //null-terminate string - rawName[rawNameSize] = '\0'; - //convert to string - std::string deviceName = rawName; - delete [] rawName; - //get deviceType - RID_DEVICE_INFO deviceInfo; - UINT deviceInfoSize = sizeof(RID_DEVICE_INFO); - GetRawInputDeviceInfo(deviceList[i].hDevice, RIDI_DEVICEINFO, (void *)&deviceInfo, &deviceInfoSize); - //check if it is a HID. we ignore keyboards and mice... - if (deviceInfo.dwType == RIM_TYPEHID) - { - //check if the vendor/product already exists in list. yes. could be more elegant... - std::vector::const_iterator cdIt = currentDevices.cbegin(); - while (cdIt != currentDevices.cend()) - { - if (cdIt->name == deviceName && cdIt->product == deviceInfo.hid.dwProductId && cdIt->vendor == deviceInfo.hid.dwVendorId) - { - //device already there - break; - } - ++cdIt; - } - //was the device found? - if (cdIt == currentDevices.cend()) - { - //no. add it. - currentDevices.push_back(InputDevice(deviceName, deviceInfo.hid.dwProductId, deviceInfo.hid.dwVendorId)); - } - } - } - } - delete [] deviceList; - } -#endif - - return currentDevices; -} - -Uint32 InputManager::devicePollingCallback(Uint32 interval, void* param) -{ - //this thing my be running in a different thread, so we're not allowed to call - //any functions or change/allocate/delete stuff, but can send a user event - SDL_Event event; - event.user.type = SDL_USEREVENT; - event.user.code = SDL_USEREVENT_POLLDEVICES; - event.user.data1 = nullptr; - event.user.data2 = nullptr; - if (!SDL_PushEvent(&event)) { - LOG(LogError) << "InputManager::devicePollingCallback - SDL event queue is full!"; - } - - return interval; -} - void InputManager::init() { - if(mJoysticks != NULL) + if(initialized()) deinit(); - //get current input devices from system - inputDevices = getInputDevices(); - - SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_TIMER); + SDL_InitSubSystem(SDL_INIT_JOYSTICK); mNumJoysticks = SDL_NumJoysticks(); - mJoysticks = new SDL_Joystick*[mNumJoysticks]; - mInputConfigs = new InputConfig*[mNumJoysticks]; - mPrevAxisValues = new std::map[mNumJoysticks]; for(int i = 0; i < mNumJoysticks; i++) { - mJoysticks[i] = SDL_JoystickOpen(i); - mInputConfigs[i] = new InputConfig(i, SDL_JoystickName(mJoysticks[i])); - - for(int k = 0; k < SDL_JoystickNumAxes(mJoysticks[i]); k++) - { - mPrevAxisValues[i][k] = 0; - } + SDL_Joystick* joy = SDL_JoystickOpen(i); + SDL_JoystickID joyId = SDL_JoystickInstanceID(joy); + mJoysticks.push_back(joy); + mInputConfigs[joyId] = new InputConfig(i, SDL_JoystickName(joy)); + + int numAxes = SDL_JoystickNumAxes(joy); + mPrevAxisValues[joyId] = new int[numAxes]; + std::fill(mPrevAxisValues[joyId], mPrevAxisValues[joyId] + numAxes, 0); //initialize array to 0 } mKeyboardInputConfig = new InputConfig(DEVICE_KEYBOARD, "Keyboard"); - SDL_JoystickEventState(SDL_ENABLE); - - //start timer for input device polling - startPolling(); - loadConfig(); -} -void InputManager::startPolling() -{ - if(devicePollingTimer != NULL) - return; - - devicePollingTimer = SDL_AddTimer(POLLING_INTERVAL, devicePollingCallback, (void *)this); -} - -void InputManager::stopPolling() -{ - if(devicePollingTimer == NULL) - return; - - SDL_RemoveTimer(devicePollingTimer); - devicePollingTimer = NULL; + SDL_JoystickEventState(SDL_ENABLE); } void InputManager::deinit() { - stopPolling(); - SDL_JoystickEventState(SDL_DISABLE); - if(!SDL_WasInit(SDL_INIT_JOYSTICK)) + if(!initialized()) return; - if(mJoysticks != NULL) + for(auto iter = mJoysticks.begin(); iter != mJoysticks.end(); iter++) { - for(int i = 0; i < mNumJoysticks; i++) - { - SDL_JoystickClose(mJoysticks[i]); - delete mInputConfigs[i]; - } - - delete[] mInputConfigs; - mInputConfigs = NULL; - - delete[] mJoysticks; - mJoysticks = NULL; - - delete mKeyboardInputConfig; - mKeyboardInputConfig = NULL; - - delete[] mPrevAxisValues; - mPrevAxisValues = NULL; + SDL_JoystickClose(*iter); } - SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_TIMER); + mJoysticks.clear(); - inputDevices.clear(); + for(auto iter = mInputConfigs.begin(); iter != mInputConfigs.end(); iter++) + { + delete iter->second; + } + + mInputConfigs.clear(); + + for(auto iter = mPrevAxisValues.begin(); iter != mPrevAxisValues.end(); iter++) + { + delete[] iter->second; + } + + mPrevAxisValues.clear(); + + if(mKeyboardInputConfig != NULL) + { + delete mKeyboardInputConfig; + mKeyboardInputConfig = NULL; + } + + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } int InputManager::getNumJoysticks() { return mNumJoysticks; } @@ -233,7 +96,7 @@ int InputManager::getButtonCountByDevice(int id) int InputManager::getNumPlayers() { return mNumPlayers; } void InputManager::setNumPlayers(int num) { mNumPlayers = num; } -InputConfig* InputManager::getInputConfigByDevice(int device) +InputConfig* InputManager::getInputConfigByDevice(SDL_JoystickID device) { if(device == DEVICE_KEYBOARD) return mKeyboardInputConfig; @@ -246,10 +109,12 @@ InputConfig* InputManager::getInputConfigByPlayer(int player) if(mKeyboardInputConfig->getPlayerNum() == player) return mKeyboardInputConfig; - for(int i = 0; i < mNumJoysticks; i++) + for(auto iter = mInputConfigs.begin(); iter != mInputConfigs.end(); iter++) { - if(mInputConfigs[i]->getPlayerNum() == player) - return mInputConfigs[i]; + if(iter->second->getPlayerNum() == player) + { + return iter->second; + } } LOG(LogError) << "Could not find input config for player number " << player << "!"; @@ -306,20 +171,10 @@ bool InputManager::parseEvent(const SDL_Event& ev) mWindow->input(getInputConfigByDevice(DEVICE_KEYBOARD), Input(DEVICE_KEYBOARD, TYPE_KEY, ev.key.keysym.sym, 0, false)); return true; - case SDL_USEREVENT: - if (ev.user.code == SDL_USEREVENT_POLLDEVICES) { - //poll joystick / HID again - std::vector currentDevices = getInputDevices(); - //compare device lists to see if devices were added/deleted - if (currentDevices != inputDevices) { - LOG(LogInfo) << "Device configuration changed!"; - inputDevices = currentDevices; - //deinit and reinit InputManager - deinit(); - init(); - } - return true; - } + case SDL_JOYDEVICEADDED: + deinit(); + init(); + return true; } return false; @@ -327,7 +182,7 @@ bool InputManager::parseEvent(const SDL_Event& ev) void InputManager::loadConfig() { - if(!mJoysticks) + if(!initialized()) { LOG(LogError) << "ERROR - cannot load InputManager config without being initialized!"; } @@ -348,10 +203,11 @@ void InputManager::loadConfig() mNumPlayers = 0; bool* configuredDevice = new bool[mNumJoysticks]; - for(int i = 0; i < mNumJoysticks; i++) + std::fill(configuredDevice, configuredDevice + mNumJoysticks, false); + + for(auto iter = mInputConfigs.begin(); iter != mInputConfigs.end(); iter++) { - mInputConfigs[i]->setPlayerNum(-1); - configuredDevice[i] = false; + iter->second->setPlayerNum(-1); } pugi::xml_node root = doc.child("inputList"); @@ -372,7 +228,8 @@ void InputManager::loadConfig() { if(!configuredDevice[i] && SDL_JoystickName(mJoysticks[i]) == devName) { - mInputConfigs[i]->loadFromXML(node, mNumPlayers); + SDL_JoystickID joyId = SDL_JoystickInstanceID(mJoysticks[i]); + mInputConfigs[joyId]->loadFromXML(node, mNumPlayers); mNumPlayers++; found = true; configuredDevice[i] = true; @@ -423,14 +280,11 @@ void InputManager::loadDefaultConfig() cfg->mapInput("mastervolup", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_PLUS, 1, true)); cfg->mapInput("mastervoldown", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_MINUS, 1, true)); - - cfg->mapInput("sortordernext", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F7, 1, true)); - cfg->mapInput("sortorderprevious", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F8, 1, true)); } void InputManager::writeConfig() { - if(!mJoysticks) + if(!initialized()) { LOG(LogError) << "ERROR - cannot write config without being initialized!"; return; @@ -457,3 +311,8 @@ std::string InputManager::getConfigPath() path += "/.emulationstation/es_input.cfg"; return path; } + +bool InputManager::initialized() const +{ + return mKeyboardInputConfig != NULL; +} diff --git a/src/InputManager.h b/src/InputManager.h index e0cef8b61..9c7ff22a3 100644 --- a/src/InputManager.h +++ b/src/InputManager.h @@ -9,16 +9,6 @@ class InputConfig; class Window; -struct InputDevice -{ - std::string name; - unsigned long vendor; - unsigned long product; - - InputDevice(const std::string & deviceName, unsigned long vendorId, unsigned long productId); - bool operator==(const InputDevice & b) const; -}; - //you should only ever instantiate one of these, by the way class InputManager { @@ -26,38 +16,23 @@ class InputManager Window* mWindow; - //non-InputManager classes shouldn't use this, as you can easily miss the keyboard - InputConfig* getInputConfigByDevice(int device); + //non-InputManager classes shouldn't use this, as you can easily miss the keyboard and don't have SDL_JoystickIDs + InputConfig* getInputConfigByDevice(SDL_JoystickID device); void loadDefaultConfig(); int mNumJoysticks; int mNumPlayers; - SDL_Joystick** mJoysticks; - InputConfig** mInputConfigs; + + std::vector mJoysticks; + std::map mInputConfigs; InputConfig* mKeyboardInputConfig; - std::map* mPrevAxisValues; - std::vector inputDevices; + std::map mPrevAxisValues; - /*! - Retrieve joysticks/ HID devices from system. - \return Returns a list of InputDevices that can be compared to the current /inputDevices to check if the configuration has changed. - \note This currently reads the content of the /dev/input on linux, searches for "js**" names and stores those. On Windows it uses GetRawInputDeviceInfo to find devices of type RIM_TYPEHID and stores those. - */ - std::vector getInputDevices() const; - - static const int POLLING_INTERVAL = 5000; - SDL_TimerID devicePollingTimer; - - /*! - Called when devicePollingTimer runs out. Sends a SDL_UserEvent with type SDL_USEREVENT_POLLDEVICES to the event queue. - */ - static Uint32 devicePollingCallback(Uint32 interval, void * param); + bool initialized() const; public: - static const int SDL_USEREVENT_POLLDEVICES = SDL_USEREVENT + 100; //This event is issued when the input devices should be rescanned. - InputManager(Window* window); ~InputManager(); @@ -77,9 +52,6 @@ public: bool parseEvent(const SDL_Event& ev); InputConfig* getInputConfigByPlayer(int player); - - void startPolling(); - void stopPolling(); }; #endif diff --git a/src/components/GuiDetectDevice.cpp b/src/components/GuiDetectDevice.cpp index f31beca8d..2777ae26d 100644 --- a/src/components/GuiDetectDevice.cpp +++ b/src/components/GuiDetectDevice.cpp @@ -44,10 +44,6 @@ bool GuiDetectDevice::input(InputConfig* config, Input input) if(!input.value) return false; - //don't allow device list to change once the first player has registered - if(mCurrentPlayer == 0) - mWindow->getInputManager()->stopPolling(); - config->setPlayerNum(mCurrentPlayer); mWindow->getInputManager()->setNumPlayers(mWindow->getInputManager()->getNumPlayers() + 1); //inc total number of players mCurrentPlayer++; diff --git a/src/components/GuiInputConfig.cpp b/src/components/GuiInputConfig.cpp index 202238fbc..15fa2f21b 100644 --- a/src/components/GuiInputConfig.cpp +++ b/src/components/GuiInputConfig.cpp @@ -38,7 +38,6 @@ bool GuiInputConfig::input(InputConfig* config, Input input) mWindow->pushGui(new GuiInputConfig(mWindow, mWindow->getInputManager()->getInputConfigByPlayer(mTargetConfig->getPlayerNum() + 1))); }else{ mWindow->getInputManager()->writeConfig(); - mWindow->getInputManager()->startPolling(); //enable polling again since we're done GuiGameList::create(mWindow); } delete this; diff --git a/src/components/TextEditComponent.cpp b/src/components/TextEditComponent.cpp index 01ae19be6..a1bb42444 100644 --- a/src/components/TextEditComponent.cpp +++ b/src/components/TextEditComponent.cpp @@ -16,6 +16,8 @@ void TextEditComponent::onFocusGained() mBox.setHorizontalImage(":/glow_hor.png"); mBox.setVerticalImage(":/glow_vert.png"); mBox.setBorderColor(0x51CCFFFF); + + SDL_StartTextInput(); } void TextEditComponent::onFocusLost() @@ -23,6 +25,8 @@ void TextEditComponent::onFocusLost() mBox.setHorizontalImage(":/glow_off_hor.png"); mBox.setVerticalImage(":/glow_off_vert.png"); mBox.setBorderColor(0xFFFFFFFF); + + SDL_StopTextInput(); } void TextEditComponent::onSizeChanged() diff --git a/src/main.cpp b/src/main.cpp index 72c87aed3..04e0e877d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -193,16 +193,16 @@ int main(int argc, char* argv[]) case SDL_KEYDOWN: case SDL_KEYUP: case SDL_JOYAXISMOTION: + case SDL_TEXTINPUT: + case SDL_TEXTEDITING: + case SDL_JOYDEVICEADDED: + case SDL_JOYDEVICEREMOVED: if(window.getInputManager()->parseEvent(event)) { sleeping = false; timeSinceLastEvent = 0; } break; - case SDL_USEREVENT: - //try to poll input devices, but do not necessarily wake up... - window.getInputManager()->parseEvent(event); - break; case SDL_QUIT: running = false; break; @@ -243,7 +243,7 @@ int main(int argc, char* argv[]) Log::flush(); } - Renderer::deinit(); + window.deinit(); SystemData::deleteSystems(); std::cout << "EmulationStation cleanly shutting down...\n";