// // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2010-2015 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 3 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "input/device_manager.hpp" #include #include #include "config/user_config.hpp" #include "graphics/irr_driver.hpp" #include "input/gamepad_device.hpp" #include "input/keyboard_device.hpp" #include "input/multitouch_device.hpp" #include "input/wiimote_manager.hpp" #include "io/file_manager.hpp" #include "states_screens/kart_selection.hpp" #include "states_screens/state_manager.hpp" #include "utils/log.hpp" #include "utils/translation.hpp" #define INPUT_MODE_DEBUG 0 static const char INPUT_FILE_NAME[] = "input.xml"; static const int INPUT_FILE_VERSION = 1; DeviceManager::DeviceManager() { m_latest_used_device = NULL; m_assign_mode = NO_ASSIGN; m_single_player = NULL; m_multitouch_device = NULL; } // DeviceManager // ----------------------------------------------------------------------------- bool DeviceManager::initialize() { GamepadConfig *gamepadConfig = NULL; GamePadDevice *gamepadDevice = NULL; m_map_fire_to_select = false; bool created = false; // Shutdown in case the device manager is being re-initialized shutdown(); if(UserConfigParams::logMisc()) { Log::info("Device manager","Initializing Device Manager"); Log::info("-","---------------------------"); } load(); // Assign a configuration to the keyboard, or create one if we haven't yet if(UserConfigParams::logMisc()) Log::info("Device manager","Initializing keyboard support."); if (m_keyboard_configs.size() == 0) { if(UserConfigParams::logMisc()) Log::info("Device manager","No keyboard configuration exists, creating one."); m_keyboard_configs.push_back(new KeyboardConfig()); created = true; } const int keyboard_amount = m_keyboard_configs.size(); for (int n = 0; n < keyboard_amount; n++) { m_keyboards.push_back(new KeyboardDevice(m_keyboard_configs.get(n))); } if(UserConfigParams::logMisc()) Log::info("Device manager","Initializing gamepad support."); irr_driver->getDevice()->activateJoysticks(m_irrlicht_gamepads); int num_gamepads = m_irrlicht_gamepads.size(); if(UserConfigParams::logMisc()) { Log::info("Device manager","Irrlicht reports %d gamepads are attached to the system.", num_gamepads); } // Create GamePadDevice for each physical gamepad and find a GamepadConfig to match for (int id = 0; id < num_gamepads; id++) { core::stringc name = m_irrlicht_gamepads[id].Name; // Some linux systems report a disk accelerometer as a gamepad, skip that if (name.find("LIS3LV02DL") != -1) continue; if(m_irrlicht_gamepads[id].HasGenericName) { // On Windows all gamepads are given the same name ('microsoft // pc-joystick driver'). Irrlicht now tries to get a better name // from the registry, but in case this should fail we still have // all gamepads with the same name shown in the GUI. This makes // configuration totally useless, so append an ID to the name. name = name + " " + StringUtils::toString(id).c_str(); } if (UserConfigParams::logMisc()) { Log::info("Device manager","#%d: %s detected...", id, name.c_str()); } // Returns true if new configuration was created if (getConfigForGamepad(id, name.c_str(), &gamepadConfig) == true) { if(UserConfigParams::logMisc()) Log::info("Device manager","creating new configuration."); created = true; } else { if(UserConfigParams::logMisc()) Log::info("Device manager","using existing configuration."); } gamepadConfig->setPlugged(); gamepadDevice = new GamePadDevice(id, name.c_str(), m_irrlicht_gamepads[id].Axes, m_irrlicht_gamepads[id].Buttons, gamepadConfig ); addGamepad(gamepadDevice); } // end for if (UserConfigParams::m_multitouch_enabled) { m_multitouch_device = new MultitouchDevice(); } if (created) save(); return created; } // initialize // ----------------------------------------------------------------------------- void DeviceManager::clearKeyboard() { m_keyboards.clearAndDeleteAll(); } // clearKeyboard // ----------------------------------------------------------------------------- void DeviceManager::clearGamepads() { m_gamepads.clearAndDeleteAll(); } // clearGamepads // ----------------------------------------------------------------------------- void DeviceManager::clearMultitouchDevices() { delete m_multitouch_device; m_multitouch_device = NULL; } // clearMultitouchDevices // ----------------------------------------------------------------------------- void DeviceManager::setAssignMode(const PlayerAssignMode assignMode) { m_assign_mode = assignMode; #if INPUT_MODE_DEBUG if (assignMode == NO_ASSIGN) Log::info("DeviceManager::setAssignMode(NO_ASSIGN)"); if (assignMode == ASSIGN) Log::info("DeviceManager::setAssignMode(ASSIGN)"); if (assignMode == DETECT_NEW) Log::info("DeviceManager::setAssignMode(DETECT_NEW)"); #endif // when going back to no-assign mode, do some cleanup if (assignMode == NO_ASSIGN) { for (unsigned int i=0; i < m_gamepads.size(); i++) { m_gamepads[i].setPlayer(NULL); } for (unsigned int i=0; i < m_keyboards.size(); i++) { m_keyboards[i].setPlayer(NULL); } if (m_multitouch_device != NULL) m_multitouch_device->setPlayer(NULL); } } // setAssignMode // ----------------------------------------------------------------------------- GamePadDevice* DeviceManager::getGamePadFromIrrID(const int id) { const int count = m_gamepads.size(); for (int i = 0; i < count; i++) { if (m_gamepads[i].getIrrIndex()== id) { return m_gamepads.get(i); } } return NULL; } // getGamePadFromIrrID // ----------------------------------------------------------------------------- /** * Check if we already have a config object for gamepad 'irr_id' as reported by * irrLicht, If no, create one. Returns true if new configuration was created, * otherwise false. */ bool DeviceManager::getConfigForGamepad(const int irr_id, const std::string& name, GamepadConfig **config) { bool found = false; bool configCreated = false; // Find appropriate configuration for(unsigned int n=0; n < m_gamepad_configs.size(); n++) { if(m_gamepad_configs[n].getName() == name) { *config = m_gamepad_configs.get(n); found = true; } } // If we can't find an appropriate configuration then create one. if (!found) { if(irr_id < (int)(m_irrlicht_gamepads.size())) { *config = new GamepadConfig( name, m_irrlicht_gamepads[irr_id].Axes, m_irrlicht_gamepads[irr_id].Buttons ); } #ifdef ENABLE_WIIUSE else // Wiimotes have a higher ID and do not refer to m_irrlicht_gamepads { // The Wiimote manager will set number of buttons and axis *config = new GamepadConfig(name.c_str()); } #endif // Add new config to list m_gamepad_configs.push_back(*config); configCreated = true; } return configCreated; } // getConfigForGamepad // ----------------------------------------------------------------------------- void DeviceManager::addKeyboard(KeyboardDevice* d) { m_keyboards.push_back(d); } // addKeyboard // ----------------------------------------------------------------------------- void DeviceManager::addEmptyKeyboard() { KeyboardConfig* newConf = new KeyboardConfig(); m_keyboard_configs.push_back(newConf); m_keyboards.push_back( new KeyboardDevice(newConf) ); } // addEmptyKeyboard // ----------------------------------------------------------------------------- void DeviceManager::addGamepad(GamePadDevice* d) { m_gamepads.push_back(d); } // addGamepad // ----------------------------------------------------------------------------- bool DeviceManager::deleteConfig(DeviceConfig* config) { for (unsigned int n=0; nprocessAndMapInput(Input::IT_KEYBOARD, button_id, mode, action)) { if (m_single_player != NULL) { *player = m_single_player; } else if (m_assign_mode == NO_ASSIGN) // Don't set the player in NO_ASSIGN mode { *player = NULL; } else { *player = keyboard->getPlayer(); } return keyboard; } } return NULL; // no appropriate binding found } // mapKeyboardInput //----------------------------------------------------------------------------- /** Helper method, only used internally. Takes care of analyzing gamepad * input. * \param[in] type Type of gamepad event (IT_STICKMOTION etc). * \param[out] player Which player this input belongs to (only set in * ASSIGN mode) * \param[out] action Which action is related to this input trigger * \param mode Used to determine whether to determine menu actions * or game actions * \return The device to which this input belongs */ InputDevice *DeviceManager::mapGamepadInput(Input::InputType type, int device_id, int button_id, int axis_dir, int *value /* inout */, InputManager::InputDriverMode mode, StateManager::ActivePlayer **player /* out */, PlayerAction *action /* out */) { GamePadDevice *gPad = getGamePadFromIrrID(device_id); if (gPad != NULL) { if (gPad->processAndMapInput(type, button_id, mode, action, value)) { if (m_single_player != NULL) { *player = m_single_player; // in single-player mode, assign the gamepad as needed if (gPad->getPlayer() != m_single_player) gPad->setPlayer(m_single_player); } else if (m_assign_mode == NO_ASSIGN) // Don't set the player in NO_ASSIGN mode { *player = NULL; } else { *player = gPad->getPlayer(); } } else gPad = NULL; // If no bind was found, return NULL } return gPad; } // mapGamepadInput //----------------------------------------------------------------------------- void DeviceManager::updateMultitouchDevice() { if (m_multitouch_device == NULL) return; if (m_single_player != NULL) { // in single-player mode, assign the gamepad as needed if (m_multitouch_device->getPlayer() != m_single_player) m_multitouch_device->setPlayer(m_single_player); } else if (m_assign_mode == NO_ASSIGN) // Don't set the player in NO_ASSIGN mode { m_multitouch_device->setPlayer(NULL); } } // updateMultitouchDevice //----------------------------------------------------------------------------- bool DeviceManager::translateInput( Input::InputType type, int device_id, int button_id, int axis_dir, int* value /* inout */, InputManager::InputDriverMode mode, StateManager::ActivePlayer** player /* out */, PlayerAction* action /* out */ ) { if (GUIEngine::getCurrentScreen() != NULL) { GUIEngine::getCurrentScreen()->filterInput(type, device_id, button_id, axis_dir, *value); } InputDevice *device = NULL; // If the input event matches a bind on an input device, get a pointer to the device switch (type) { case Input::IT_KEYBOARD: device = mapKeyboardInput(button_id, mode, player, action); // If the action is not recognised, check if it's a fire key // that should be mapped to select if(!device && m_map_fire_to_select) { device = mapKeyboardInput(button_id, InputManager::INGAME, player, action); if(device && *action == PA_FIRE) *action = PA_MENU_SELECT; } break; case Input::IT_STICKBUTTON: case Input::IT_STICKMOTION: device = mapGamepadInput(type, device_id, button_id, axis_dir, value, mode, player, action); if(!device && m_map_fire_to_select) { device = mapGamepadInput(type, device_id, button_id, axis_dir, value, InputManager::INGAME, player, action); if(device && *action == PA_FIRE) *action = PA_MENU_SELECT; } break; default: break; }; // Return true if input was successfully translated to an action and player if (device != NULL && abs(*value) > Input::MAX_VALUE/2) { m_latest_used_device = device; } return (device != NULL); } // translateInput //----------------------------------------------------------------------------- InputDevice* DeviceManager::getLatestUsedDevice() { // If none, probably the user clicked or used enter; give keyboard by default if (m_latest_used_device == NULL) { //Log::info("DeviceManager", "No latest device, returning keyboard); return m_keyboards.get(0); // FIXME: is this right? } return m_latest_used_device; } // getLatestUsedDevice // ----------------------------------------------------------------------------- void DeviceManager::clearLatestUsedDevice() { m_latest_used_device = NULL; } // clearLatestUsedDevice // ----------------------------------------------------------------------------- /** Loads the configuration from the user's input.xml file. */ bool DeviceManager::load() { std::string filepath = file_manager->getUserConfigFile(INPUT_FILE_NAME); if(UserConfigParams::logMisc()) Log::info("Device manager","Loading input.xml..."); const XMLNode *input = file_manager->createXMLTree(filepath); if(!input) { if(UserConfigParams::logMisc()) Log::warn("Device manager","No configuration file exists."); return false; } if(input->getName()!="input") { Log::warn("DeviceManager", "Invalid input.xml file - no input node."); delete input; return false; } int version=0; if(!input->get("version", &version) || version != INPUT_FILE_VERSION ) { //I18N: shown when config file is too old GUIEngine::showMessage(_("Please re-configure your key bindings.")); GUIEngine::showMessage(_("Your input config file is not compatible " "with this version of STK.")); delete input; return false; } for(unsigned int i=0; igetNumNodes(); i++) { const XMLNode *config = input->getNode(i); DeviceConfig *device_config = DeviceConfig::create(config); if(!device_config) { Log::warn("DeviceManager", "Invalid node '%s' in input.xml - ignored.", config->getName().c_str()); continue; } if(config->getName()=="keyboard") { KeyboardConfig *kc = static_cast(device_config); m_keyboard_configs.push_back(kc); } else if (config->getName()=="gamepad") { GamepadConfig *gc = static_cast(device_config); m_gamepad_configs.push_back(gc); } } // for i < getNumNodes if (UserConfigParams::logMisc()) { Log::info("Device manager", "Found %d keyboard and %d gamepad configurations.", m_keyboard_configs.size(), m_gamepad_configs.size()); } // For Debugging.... /* for (int n = 0; n < m_keyboard_configs.size(); n++) printf("Config #%d\n%s", n + 1, m_keyboard_configs[n].toString().c_str()); for (int n = 0; n < m_gamepad_configs.size(); n++) printf("%s", m_gamepad_configs[n].toString().c_str()); */ delete input; return true; } // load // ----------------------------------------------------------------------------- void DeviceManager::save() { static std::string filepath = file_manager->getUserConfigFile(INPUT_FILE_NAME); if(UserConfigParams::logMisc()) Log::info("Device manager","Serializing input.xml..."); std::ofstream configfile; configfile.open (filepath.c_str()); if(!configfile.is_open()) { Log::error("Device manager","Failed to open %s for writing, controls won't be saved",filepath.c_str()); return; } configfile << "\n\n"; for(unsigned int n=0; n\n"; configfile.close(); if(UserConfigParams::logMisc()) Log::info("Device manager","Serialization complete."); } // save // ----------------------------------------------------------------------------- KeyboardDevice* DeviceManager::getKeyboardFromBtnID(const int button_id) { const int keyboard_amount = m_keyboards.size(); for (int n=0; nhasBindingFor(button_id)) return m_keyboards.get(n); } return NULL; } // getKeyboardFromButtonID // ----------------------------------------------------------------------------- void DeviceManager::shutdown() { m_gamepads.clearAndDeleteAll(); m_keyboards.clearAndDeleteAll(); m_gamepad_configs.clearAndDeleteAll(); m_keyboard_configs.clearAndDeleteAll(); delete m_multitouch_device; m_multitouch_device = NULL; m_latest_used_device = NULL; } // shutdown