Merge branch 'master' of https://code.google.com/p/supertux
[supertux.git] / src / control / keyboard_manager.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>,
3 //                2007-2014 Ingo Ruhnke <grumbel@gmail.com>
4 //
5 //  This program is free software: you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation, either version 3 of the License, or
8 //  (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, see <http://www.gnu.org/licenses/>.
17
18 #include "control/keyboard_manager.hpp"
19
20 #include "control/controller.hpp"
21 #include "control/joystick_manager.hpp"
22 #include "gui/menu_manager.hpp"
23 #include "lisp/list_iterator.hpp"
24 #include "supertux/console.hpp"
25 #include "supertux/menu/joystick_menu.hpp"
26 #include "supertux/menu/keyboard_menu.hpp"
27 #include "supertux/menu/menu_storage.hpp"
28 #include "util/writer.hpp"
29
30 KeyboardManager::KeyboardManager(InputManager* parent) :
31   m_parent(parent),
32   keymap(),
33   jump_with_up_kbd(false),
34   wait_for_key(-1)
35 {
36   // initialize default keyboard map
37   keymap[SDLK_LEFT]     = Controller::LEFT;
38   keymap[SDLK_RIGHT]    = Controller::RIGHT;
39   keymap[SDLK_UP]       = Controller::UP;
40   keymap[SDLK_DOWN]     = Controller::DOWN;
41   keymap[SDLK_SPACE]    = Controller::JUMP;
42   keymap[SDLK_LCTRL]    = Controller::ACTION;
43   keymap[SDLK_LALT]     = Controller::ACTION;
44   keymap[SDLK_ESCAPE]   = Controller::PAUSE_MENU;
45   keymap[SDLK_p]        = Controller::PAUSE_MENU;
46   keymap[SDLK_PAUSE]    = Controller::PAUSE_MENU;
47   keymap[SDLK_RETURN]   = Controller::MENU_SELECT;
48   keymap[SDLK_KP_ENTER] = Controller::MENU_SELECT;
49   keymap[SDLK_CARET]    = Controller::CONSOLE;
50   keymap[SDLK_DELETE]   = Controller::PEEK_LEFT;
51   keymap[SDLK_PAGEDOWN] = Controller::PEEK_RIGHT;
52   keymap[SDLK_HOME]     = Controller::PEEK_UP;
53   keymap[SDLK_END]      = Controller::PEEK_DOWN;
54   keymap[SDLK_TAB]      = Controller::CHEAT_MENU;
55 }
56
57 KeyboardManager::~KeyboardManager()
58 {
59 }
60
61 void
62 KeyboardManager::process_key_event(const SDL_KeyboardEvent& event)
63 {
64   KeyMap::iterator key_mapping = keymap.find(event.keysym.sym);
65
66   // if console key was pressed: toggle console
67   if (key_mapping != keymap.end() &&
68       key_mapping->second == Controller::CONSOLE)
69   {
70     if (event.type == SDL_KEYDOWN)
71     {
72       Console::instance->toggle();
73     }
74   }
75   else if (Console::instance->hasFocus())
76   {
77     // if console is open: send key there
78     process_console_key_event(event);
79   }
80   else if (MenuManager::instance().is_active())
81   {
82     // if menu mode: send key there
83     process_menu_key_event(event);
84   }
85   else if (key_mapping == keymap.end())
86   {
87     // default action: update controls
88     //log_debug << "Key " << event.key.SDL_Keycode.sym << " is unbound" << std::endl;
89   }
90   else
91   {
92     auto control = key_mapping->second;
93     bool value = (event.type == SDL_KEYDOWN);
94     m_parent->get_controller()->set_control(control, value);
95     if (jump_with_up_kbd && control == Controller::UP)
96     {
97       m_parent->get_controller()->set_control(Controller::JUMP, value);
98     }
99   }
100 }
101
102 void
103 KeyboardManager::process_text_input_event(const SDL_TextInputEvent& event)
104 {
105   if (Console::instance->hasFocus()) {
106     for(int i = 0; event.text[i] != '\0'; ++i)
107     {
108       Console::instance->input(event.text[i]);
109     }
110   }
111 }
112
113 void
114 KeyboardManager::process_console_key_event(const SDL_KeyboardEvent& event)
115 {
116   if (event.type != SDL_KEYDOWN) return;
117
118   switch (event.keysym.sym) {
119     case SDLK_RETURN:
120       Console::instance->enter();
121       break;
122     case SDLK_BACKSPACE:
123       Console::instance->backspace();
124       break;
125     case SDLK_TAB:
126       Console::instance->autocomplete();
127       break;
128     case SDLK_PAGEUP:
129       Console::instance->scroll(-1);
130       break;
131     case SDLK_PAGEDOWN:
132       Console::instance->scroll(+1);
133       break;
134     case SDLK_HOME:
135       Console::instance->move_cursor(-65535);
136       break;
137     case SDLK_END:
138       Console::instance->move_cursor(+65535);
139       break;
140     case SDLK_UP:
141       Console::instance->show_history(-1);
142       break;
143     case SDLK_DOWN:
144       Console::instance->show_history(+1);
145       break;
146     case SDLK_LEFT:
147       Console::instance->move_cursor(-1);
148       break;
149     case SDLK_RIGHT:
150       Console::instance->move_cursor(+1);
151       break;
152     default:
153       break;
154   }
155 }
156
157 void
158 KeyboardManager::process_menu_key_event(const SDL_KeyboardEvent& event)
159 {
160   // wait for key mode?
161   if (wait_for_key >= 0)
162   {
163     if (event.type == SDL_KEYUP)
164       return;
165
166     if (event.keysym.sym != SDLK_ESCAPE &&
167         event.keysym.sym != SDLK_PAUSE)
168     {
169       bind_key(event.keysym.sym, static_cast<Controller::Control>(wait_for_key));
170     }
171     m_parent->reset();
172     MenuManager::instance().refresh();
173     wait_for_key = -1;
174     return;
175   }
176
177   if (m_parent->joystick_manager->wait_for_joystick >= 0)
178   {
179     if (event.keysym.sym == SDLK_ESCAPE)
180     {
181       m_parent->reset();
182       MenuManager::instance().refresh();
183       m_parent->joystick_manager->wait_for_joystick = -1;
184     }
185     return;
186   }
187
188   Controller::Control control;
189   /* we use default keys when the menu is open (to avoid problems when
190    * redefining keys to invalid settings
191    */
192   switch(event.keysym.sym) {
193     case SDLK_UP:
194       control = Controller::UP;
195       break;
196     case SDLK_DOWN:
197       control = Controller::DOWN;
198       break;
199     case SDLK_LEFT:
200       control = Controller::LEFT;
201       break;
202     case SDLK_RIGHT:
203       control = Controller::RIGHT;
204       break;
205     case SDLK_SPACE:
206     case SDLK_RETURN:
207     case SDLK_KP_ENTER:
208       control = Controller::MENU_SELECT;
209       break;
210     case SDLK_ESCAPE:
211     case SDLK_PAUSE:
212       control = Controller::PAUSE_MENU;
213       break;
214     default:
215       return;
216       break;
217   }
218
219   m_parent->get_controller()->set_control(control, (event.type == SDL_KEYDOWN));
220 }
221
222 void
223 KeyboardManager::bind_key(SDL_Keycode key, Controller::Control control)
224 {
225   // remove all previous mappings for that control and for that key
226   for(KeyMap::iterator i = keymap.begin();
227       i != keymap.end(); /* no ++i */) {
228     if (i->second == control) {
229       KeyMap::iterator e = i;
230       ++i;
231       keymap.erase(e);
232     } else {
233       ++i;
234     }
235   }
236
237   KeyMap::iterator i = keymap.find(key);
238   if (i != keymap.end())
239     keymap.erase(i);
240
241   // add new mapping
242   keymap[key] = control;
243 }
244
245 SDL_Keycode
246 KeyboardManager::reversemap_key(Controller::Control c)
247 {
248   for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i)
249   {
250     if (i->second == c)
251     {
252       return i->first;
253     }
254   }
255
256   return SDLK_UNKNOWN;
257 }
258
259 void
260 KeyboardManager::read(const lisp::Lisp* keymap_lisp)
261 {
262   // keycode values changed between SDL1 and SDL2, so we skip old SDL1
263   // based values and use the defaults instead on the first read of
264   // the config file
265   bool config_is_sdl2 = false;
266   keymap_lisp->get("sdl2", config_is_sdl2);
267   if (config_is_sdl2)
268   {
269     keymap.clear();
270     keymap_lisp->get("jump-with-up", jump_with_up_kbd);
271     lisp::ListIterator iter(keymap_lisp);
272     while(iter.next()) {
273       if (iter.item() == "map") {
274         int key = -1;
275         std::string control;
276         const lisp::Lisp* map = iter.lisp();
277         map->get("key", key);
278
279         map->get("control", control);
280
281         int i = 0;
282         for(i = 0; Controller::controlNames[i] != 0; ++i) {
283           if (control == Controller::controlNames[i])
284             break;
285         }
286         if (Controller::controlNames[i] == 0) {
287           log_info << "Invalid control '" << control << "' in keymap" << std::endl;
288           continue;
289         }
290         keymap[static_cast<SDL_Keycode>(key)] = static_cast<Controller::Control>(i);
291       }
292     }
293   }
294 }
295
296 void
297 KeyboardManager::write(Writer& writer)
298 {
299   writer.write("sdl2", true);
300   writer.write("jump-with-up", jump_with_up_kbd);
301   for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) {
302     writer.start_list("map");
303     writer.write("key", (int) i->first);
304     writer.write("control", Controller::controlNames[i->second]);
305     writer.end_list("map");
306   }
307 }
308
309 /* EOF */