1 //
2 // SuperTuxKart - a fun racing game with go-kart
3 // Copyright (C) 2010-2015 SuperTuxKart-Team
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 3
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 #include "input/device_manager.hpp"
20
21 #include <iostream>
22 #include <fstream>
23
24 #include "config/user_config.hpp"
25 #include "graphics/irr_driver.hpp"
26 #include "input/gamepad_device.hpp"
27 #include "input/keyboard_device.hpp"
28 #include "input/multitouch_device.hpp"
29 #include "input/wiimote_manager.hpp"
30 #include "io/file_manager.hpp"
31 #include "states_screens/kart_selection.hpp"
32 #include "states_screens/state_manager.hpp"
33 #include "utils/file_utils.hpp"
34 #include "utils/log.hpp"
35 #include "utils/string_utils.hpp"
36 #include "utils/translation.hpp"
37
38 #define INPUT_MODE_DEBUG 0
39
40 static const char INPUT_FILE_NAME[] = "input.xml";
41 static const int INPUT_FILE_VERSION = 1;
42
DeviceManager()43 DeviceManager::DeviceManager()
44 {
45 m_latest_used_device = NULL;
46 m_assign_mode = NO_ASSIGN;
47 m_single_player = NULL;
48 m_multitouch_device = NULL;
49 } // DeviceManager
50
51 // -----------------------------------------------------------------------------
~DeviceManager()52 DeviceManager::~DeviceManager()
53 {
54 delete m_multitouch_device;
55 } // ~DeviceManager
56
57 // -----------------------------------------------------------------------------
initialize()58 bool DeviceManager::initialize()
59 {
60 m_map_fire_to_select = false;
61 bool created = false;
62
63
64 // Shutdown in case the device manager is being re-initialized
65 shutdown();
66
67 if(UserConfigParams::logMisc())
68 {
69 Log::info("Device manager","Initializing Device Manager");
70 Log::info("-","---------------------------");
71 }
72
73 load();
74
75 // Assign a configuration to the keyboard, or create one if we haven't yet
76 if(UserConfigParams::logMisc()) Log::info("Device manager","Initializing keyboard support.");
77 if (m_keyboard_configs.size() == 0)
78 {
79 if(UserConfigParams::logMisc())
80 Log::info("Device manager","No keyboard configuration exists, creating one.");
81 m_keyboard_configs.push_back(new KeyboardConfig());
82
83 created = true;
84 }
85
86 const int keyboard_amount = m_keyboard_configs.size();
87 for (int n = 0; n < keyboard_amount; n++)
88 {
89 m_keyboards.push_back(new KeyboardDevice(m_keyboard_configs.get(n)));
90 }
91
92 if ((UserConfigParams::m_multitouch_active == 1 &&
93 irr_driver->getDevice()->supportsTouchDevice()) ||
94 UserConfigParams::m_multitouch_active > 1)
95 {
96 m_multitouch_device = new MultitouchDevice();
97 }
98
99 if (created) save();
100
101 return created;
102 } // initialize
103
104 // -----------------------------------------------------------------------------
clearKeyboard()105 void DeviceManager::clearKeyboard()
106 {
107 m_keyboards.clearAndDeleteAll();
108 } // clearKeyboard
109
110 // -----------------------------------------------------------------------------
clearGamepads()111 void DeviceManager::clearGamepads()
112 {
113 m_gamepads.clearAndDeleteAll();
114 } // clearGamepads
115 // -----------------------------------------------------------------------------
clearMultitouchDevices()116 void DeviceManager::clearMultitouchDevices()
117 {
118 delete m_multitouch_device;
119 m_multitouch_device = NULL;
120 } // clearMultitouchDevices
121
122 // -----------------------------------------------------------------------------
setAssignMode(const PlayerAssignMode assignMode)123 void DeviceManager::setAssignMode(const PlayerAssignMode assignMode)
124 {
125 m_assign_mode = assignMode;
126
127 #if INPUT_MODE_DEBUG
128 if (assignMode == NO_ASSIGN) Log::info("DeviceManager::setAssignMode(NO_ASSIGN)");
129 if (assignMode == ASSIGN) Log::info("DeviceManager::setAssignMode(ASSIGN)");
130 if (assignMode == DETECT_NEW) Log::info("DeviceManager::setAssignMode(DETECT_NEW)");
131 #endif
132
133 // when going back to no-assign mode, do some cleanup
134 if (assignMode == NO_ASSIGN)
135 {
136 for (unsigned int i=0; i < m_gamepads.size(); i++)
137 {
138 m_gamepads[i].setPlayer(NULL);
139 }
140 for (unsigned int i=0; i < m_keyboards.size(); i++)
141 {
142 m_keyboards[i].setPlayer(NULL);
143 }
144
145 if (m_multitouch_device != NULL)
146 m_multitouch_device->setPlayer(NULL);
147 }
148 } // setAssignMode
149
150 // -----------------------------------------------------------------------------
getGamePadFromIrrID(const int id)151 GamePadDevice* DeviceManager::getGamePadFromIrrID(const int id)
152 {
153 const int count = m_gamepads.size();
154 for (int i = 0; i < count; i++)
155 {
156 if (m_gamepads[i].getIrrIndex()== id)
157 {
158
159 return m_gamepads.get(i);
160 }
161 }
162 return NULL;
163 } // getGamePadFromIrrID
164
165 // -----------------------------------------------------------------------------
166 /**
167 * Check if we already have a config object for gamepad 'irr_id' as reported by
168 * irrLicht, If no, create one. Returns true if new configuration was created,
169 * otherwise false.
170 */
getConfigForGamepad(const int irr_id,const std::string & name,GamepadConfig ** config)171 bool DeviceManager::getConfigForGamepad(const int irr_id,
172 const std::string& name,
173 GamepadConfig **config)
174 {
175 bool found = false;
176 bool configCreated = false;
177
178 // Find appropriate configuration
179 for(unsigned int n=0; n < m_gamepad_configs.size(); n++)
180 {
181 if(m_gamepad_configs[n].getName() == name)
182 {
183 *config = m_gamepad_configs.get(n);
184 found = true;
185 }
186 }
187
188 // If we can't find an appropriate configuration then create one.
189 if (!found)
190 {
191 // The Wiimote manager and SDL controller will set number of buttons and
192 // axes
193 *config = new GamepadConfig(name.c_str());
194 // Add new config to list
195 m_gamepad_configs.push_back(*config);
196 configCreated = true;
197 }
198
199 return configCreated;
200 } // getConfigForGamepad
201
202 // -----------------------------------------------------------------------------
addKeyboard(KeyboardDevice * d)203 void DeviceManager::addKeyboard(KeyboardDevice* d)
204 {
205 m_keyboards.push_back(d);
206 } // addKeyboard
207
208 // -----------------------------------------------------------------------------
addEmptyKeyboard()209 void DeviceManager::addEmptyKeyboard()
210 {
211 KeyboardConfig* newConf = new KeyboardConfig();
212 m_keyboard_configs.push_back(newConf);
213 m_keyboards.push_back( new KeyboardDevice(newConf) );
214 } // addEmptyKeyboard
215
216 // -----------------------------------------------------------------------------
217
addGamepad(GamePadDevice * d)218 void DeviceManager::addGamepad(GamePadDevice* d)
219 {
220 m_gamepads.push_back(d);
221 } // addGamepad
222
223 // -----------------------------------------------------------------------------
224
deleteConfig(DeviceConfig * config)225 bool DeviceManager::deleteConfig(DeviceConfig* config)
226 {
227 for (unsigned int n=0; n<m_keyboards.size(); n++)
228 {
229 if (m_keyboards[n].getConfiguration() == config)
230 {
231 m_keyboards.erase(n);
232 n--;
233 }
234 }
235 for (unsigned int n=0; n<m_gamepads.size(); n++)
236 {
237 if (m_gamepads[n].getConfiguration() == config)
238 {
239 m_gamepads.erase(n);
240 n--;
241 }
242 }
243
244 if (m_keyboard_configs.erase(config))
245 {
246 return true;
247 }
248
249 if (m_gamepad_configs.erase(config))
250 {
251 return true;
252 }
253
254 return false;
255 } // deleteConfig
256
257 // -----------------------------------------------------------------------------
258 /** Helper method, only used internally. Takes care of analyzing keyboard input.
259 * \param[in] button_id Id of the key pressed.
260 * \param[in] mode Used to determine whether to determine menu actions
261 * or game actions
262 * \param[out] player Which player this input belongs to (only set in
263 * ASSIGN mode).
264 * \param[out] action Which action is related to this input trigger.
265 * \return The device to which this input belongs
266 */
mapKeyboardInput(int button_id,InputManager::InputDriverMode mode,StateManager::ActivePlayer ** player,PlayerAction * action)267 InputDevice* DeviceManager::mapKeyboardInput(int button_id,
268 InputManager::InputDriverMode mode,
269 StateManager::ActivePlayer **player,
270 PlayerAction *action /* out */)
271 {
272 const int keyboard_amount = m_keyboards.size();
273
274 for (int n=0; n<keyboard_amount; n++)
275 {
276 KeyboardDevice *keyboard = m_keyboards.get(n);
277
278 if (keyboard->processAndMapInput(Input::IT_KEYBOARD, button_id, mode, action))
279 {
280 if (m_single_player != NULL)
281 {
282 *player = m_single_player;
283 }
284 else if (m_assign_mode == NO_ASSIGN) // Don't set the player in NO_ASSIGN mode
285 {
286 *player = NULL;
287 }
288 else
289 {
290 *player = keyboard->getPlayer();
291 }
292 return keyboard;
293 }
294 }
295
296 return NULL; // no appropriate binding found
297 } // mapKeyboardInput
298
299 //-----------------------------------------------------------------------------
300 /** Helper method, only used internally. Takes care of analyzing gamepad
301 * input.
302 * \param[in] type Type of gamepad event (IT_STICKMOTION etc).
303 * \param[out] player Which player this input belongs to (only set in
304 * ASSIGN mode)
305 * \param[out] action Which action is related to this input trigger
306 * \param mode Used to determine whether to determine menu actions
307 * or game actions
308 * \return The device to which this input belongs
309 */
310
mapGamepadInput(Input::InputType type,int device_id,int button_id,int axis_dir,int * value,InputManager::InputDriverMode mode,StateManager::ActivePlayer ** player,PlayerAction * action)311 InputDevice *DeviceManager::mapGamepadInput(Input::InputType type,
312 int device_id,
313 int button_id,
314 int axis_dir,
315 int *value /* inout */,
316 InputManager::InputDriverMode mode,
317 StateManager::ActivePlayer **player /* out */,
318 PlayerAction *action /* out */)
319 {
320 GamePadDevice *gPad = getGamePadFromIrrID(device_id);
321
322 if (gPad != NULL)
323 {
324 // Ignore deadzone events if this isn't the latest used device in
325 // single-player mode (allowing the player to switch device at any time)
326 int dz = static_cast<GamepadConfig*>(gPad->getConfiguration())->getDeadzone();
327 if (m_single_player != NULL && m_latest_used_device != gPad
328 && *value > -dz && *value < dz)
329 {
330 *player = NULL;
331 return NULL;
332 }
333
334 if (gPad->processAndMapInput(type, button_id, mode, action, value))
335 {
336 if (m_single_player != NULL)
337 {
338 *player = m_single_player;
339
340 // in single-player mode, assign the gamepad as needed
341 if (gPad->getPlayer() != m_single_player) gPad->setPlayer(m_single_player);
342 }
343 else if (m_assign_mode == NO_ASSIGN) // Don't set the player in NO_ASSIGN mode
344 {
345 *player = NULL;
346 }
347 else
348 {
349 *player = gPad->getPlayer();
350 }
351 }
352 else gPad = NULL; // If no bind was found, return NULL
353 }
354
355 return gPad;
356 } // mapGamepadInput
357
358 //-----------------------------------------------------------------------------
359
updateMultitouchDevice()360 void DeviceManager::updateMultitouchDevice()
361 {
362 if (m_multitouch_device == NULL)
363 return;
364
365 if (m_single_player != NULL)
366 {
367 // in single-player mode, assign the gamepad as needed
368 if (m_multitouch_device->getPlayer() != m_single_player)
369 m_multitouch_device->setPlayer(m_single_player);
370 }
371 else
372 {
373 m_multitouch_device->setPlayer(NULL);
374 }
375
376 m_multitouch_device->updateController();
377 } // updateMultitouchDevice
378
379 //-----------------------------------------------------------------------------
380
translateInput(Input::InputType type,int device_id,int button_id,int axis_dir,int * value,InputManager::InputDriverMode mode,StateManager::ActivePlayer ** player,PlayerAction * action)381 bool DeviceManager::translateInput( Input::InputType type,
382 int device_id,
383 int button_id,
384 int axis_dir,
385 int* value /* inout */,
386 InputManager::InputDriverMode mode,
387 StateManager::ActivePlayer** player /* out */,
388 PlayerAction* action /* out */ )
389 {
390 if (GUIEngine::getCurrentScreen() != NULL)
391 {
392 GUIEngine::getCurrentScreen()->filterInput(type, device_id, button_id, axis_dir, *value);
393 }
394
395 InputDevice *device = NULL;
396
397 // If the input event matches a bind on an input device, get a pointer to the device
398 switch (type)
399 {
400 case Input::IT_KEYBOARD:
401 device = mapKeyboardInput(button_id, mode, player, action);
402 // If the action is not recognised, check if it's a fire key
403 // that should be mapped to select
404 if(!device && m_map_fire_to_select)
405 {
406 PlayerAction fire_action = PA_BEFORE_FIRST;
407 device = mapKeyboardInput(button_id, InputManager::INGAME, player,
408 &fire_action);
409 if (device && fire_action == PA_FIRE)
410 *action = PA_MENU_SELECT;
411 else
412 device = NULL;
413 }
414 break;
415 case Input::IT_STICKBUTTON:
416 case Input::IT_STICKMOTION:
417 device = mapGamepadInput(type, device_id, button_id, axis_dir, value,
418 mode, player, action);
419 if(!device && m_map_fire_to_select)
420 {
421 PlayerAction fire_action = PA_BEFORE_FIRST;
422 device = mapGamepadInput(type, device_id, button_id, axis_dir, value,
423 InputManager::INGAME, player, &fire_action);
424 if (device && fire_action == PA_FIRE)
425 *action = PA_MENU_SELECT;
426 else
427 device = NULL;
428 }
429 break;
430 default:
431 break;
432 };
433
434
435 // Return true if input was successfully translated to an action and player
436 if (device != NULL && abs(*value) > Input::MAX_VALUE/2)
437 {
438 m_latest_used_device = device;
439 }
440
441 return (device != NULL);
442 } // translateInput
443
444 //-----------------------------------------------------------------------------
getLatestUsedDevice()445 InputDevice* DeviceManager::getLatestUsedDevice()
446 {
447 // If none, probably the user clicked or used enter; give keyboard by default
448
449 if (m_latest_used_device == NULL)
450 {
451 //Log::info("DeviceManager", "No latest device, returning keyboard);
452 return m_keyboards.get(0); // FIXME: is this right?
453 }
454
455 return m_latest_used_device;
456 } // getLatestUsedDevice
457
458 // -----------------------------------------------------------------------------
clearLatestUsedDevice()459 void DeviceManager::clearLatestUsedDevice()
460 {
461 m_latest_used_device = NULL;
462 } // clearLatestUsedDevice
463
464 // -----------------------------------------------------------------------------
465 /** Loads the configuration from the user's input.xml file.
466 */
load()467 bool DeviceManager::load()
468 {
469 std::string filepath = file_manager->getUserConfigFile(INPUT_FILE_NAME);
470
471 if(UserConfigParams::logMisc())
472 Log::info("Device manager","Loading input.xml...");
473
474 const XMLNode *input = file_manager->createXMLTree(filepath);
475
476 if(!input)
477 {
478 if(UserConfigParams::logMisc())
479 Log::warn("Device manager","No configuration file exists.");
480 return false;
481 }
482
483 if(input->getName()!="input")
484 {
485 Log::warn("DeviceManager", "Invalid input.xml file - no input node.");
486 delete input;
487 return false;
488 }
489
490 int version=0;
491 if(!input->get("version", &version) || version != INPUT_FILE_VERSION )
492 {
493 //I18N: shown when config file is too old
494 GUIEngine::showMessage(_("Please re-configure your key bindings."));
495 GUIEngine::showMessage(_("Your input config file is not compatible "
496 "with this version of STK."));
497 delete input;
498 return false;
499 }
500
501 for(unsigned int i=0; i<input->getNumNodes(); i++)
502 {
503 const XMLNode *config = input->getNode(i);
504 DeviceConfig *device_config = DeviceConfig::create(config);
505 if(!device_config)
506 {
507 Log::warn("DeviceManager",
508 "Invalid node '%s' in input.xml - ignored.",
509 config->getName().c_str());
510 continue;
511 }
512 if (config->getName() == "keyboard")
513 {
514 KeyboardConfig *kc = static_cast<KeyboardConfig*>(device_config);
515 m_keyboard_configs.push_back(kc);
516 }
517 else if (config->getName() == "gamepad")
518 {
519 GamepadConfig *gc = static_cast<GamepadConfig*>(device_config);
520 m_gamepad_configs.push_back(gc);
521 }
522 } // for i < getNumNodes
523
524 if (UserConfigParams::logMisc())
525 {
526 Log::info("Device manager",
527 "Found %d keyboard and %d gamepad configurations.",
528 m_keyboard_configs.size(), m_gamepad_configs.size());
529 }
530
531 // For Debugging....
532 /*
533 for (int n = 0; n < m_keyboard_configs.size(); n++)
534 printf("Config #%d\n%s", n + 1, m_keyboard_configs[n].toString().c_str());
535
536 for (int n = 0; n < m_gamepad_configs.size(); n++)
537 printf("%s", m_gamepad_configs[n].toString().c_str());
538 */
539
540 delete input;
541
542 return true;
543 } // load
544
545 // -----------------------------------------------------------------------------
save()546 void DeviceManager::save()
547 {
548 static std::string filepath = file_manager->getUserConfigFile(INPUT_FILE_NAME);
549 if(UserConfigParams::logMisc()) Log::info("Device manager","Serializing input.xml...");
550
551
552 std::ofstream configfile(FileUtils::getPortableWritingPath(filepath));
553
554 if(!configfile.is_open())
555 {
556 Log::error("Device manager","Failed to open %s for writing, controls won't be saved",filepath.c_str());
557 return;
558 }
559
560
561 configfile << "<input version=\"" << INPUT_FILE_VERSION << "\">\n\n";
562
563 configfile << "<!--\n"
564 << "Event 1 : Keyboard button press\n"
565 << " 'id' indicates which button, as defined by irrlicht's EKEY_CODE enum\n"
566 << " 'character' contains the associated unicode character.\n"
567 << " Only used as fallback when displaying special characters in the UI.\n"
568 << "Event 2 : Gamepad stick motion\n"
569 << " 'id' indicates which stick, starting from 0\n"
570 << " 'direction' 0 means negative, 1 means positive\n"
571 << "Event 3 : Gamepad button press\n"
572 << " 'id' indicates which button, starting from 0\n"
573 << "-->\n\n";
574
575 for(unsigned int n=0; n<m_keyboard_configs.size(); n++)
576 {
577 m_keyboard_configs[n].save(configfile);
578 }
579 for(unsigned int n=0; n<m_gamepad_configs.size(); n++)
580 {
581 m_gamepad_configs[n].save(configfile);
582 }
583
584 configfile << "</input>\n";
585 configfile.close();
586 if(UserConfigParams::logMisc()) Log::info("Device manager","Serialization complete.");
587 } // save
588
589 // -----------------------------------------------------------------------------
590
getKeyboardFromBtnID(const int button_id)591 KeyboardDevice* DeviceManager::getKeyboardFromBtnID(const int button_id)
592 {
593 const int keyboard_amount = m_keyboards.size();
594 for (int n=0; n<keyboard_amount; n++)
595 {
596 if (m_keyboards[n].getConfiguration()->hasBindingFor(button_id))
597 return m_keyboards.get(n);
598 }
599
600 return NULL;
601 } // getKeyboardFromButtonID
602
603 // -----------------------------------------------------------------------------
604
shutdown()605 void DeviceManager::shutdown()
606 {
607 m_gamepads.clearAndDeleteAll();
608 m_keyboards.clearAndDeleteAll();
609 m_gamepad_configs.clearAndDeleteAll();
610 m_keyboard_configs.clearAndDeleteAll();
611 delete m_multitouch_device;
612 m_multitouch_device = NULL;
613 m_latest_used_device = NULL;
614 } // shutdown
615