912a02e91f71abd9d5b0c2e90c83c35519d2d52a
[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 }
55
56 KeyboardManager::~KeyboardManager()
57 {
58 }
59
60 void
61 KeyboardManager::process_key_event(const SDL_KeyboardEvent& event)
62 {
63   KeyMap::iterator key_mapping = keymap.find(event.keysym.sym);
64
65   // if console key was pressed: toggle console
66   if (key_mapping != keymap.end() && 
67       key_mapping->second == Controller::CONSOLE)
68   {
69     if (event.type == SDL_KEYDOWN)
70     {
71       Console::instance->toggle();
72     }
73   } 
74   else if (Console::instance->hasFocus()) 
75   {
76     // if console is open: send key there
77     process_console_key_event(event);
78   }
79   else if (MenuManager::instance().is_active())
80   {
81     // if menu mode: send key there
82     process_menu_key_event(event);
83   }
84   else if (key_mapping == keymap.end()) 
85   {
86     // default action: update controls
87     //log_debug << "Key " << event.key.SDL_Keycode.sym << " is unbound" << std::endl;
88   }
89   else 
90   {
91     auto control = key_mapping->second;
92     bool value = (event.type == SDL_KEYDOWN);
93     m_parent->get_controller()->set_control(control, value);
94     if (jump_with_up_kbd && control == Controller::UP)
95     {
96       m_parent->get_controller()->set_control(Controller::JUMP, value);
97     }
98   }
99 }
100
101 void
102 KeyboardManager::process_text_input_event(const SDL_TextInputEvent& event)
103 {
104   if (Console::instance->hasFocus()) {
105     for(int i = 0; event.text[i] != '\0'; ++i)
106     {
107       Console::instance->input(event.text[i]);
108     }
109   }
110 }
111
112 void
113 KeyboardManager::process_console_key_event(const SDL_KeyboardEvent& event)
114 {
115   if (event.type != SDL_KEYDOWN) return;
116
117   switch (event.keysym.sym) {
118     case SDLK_RETURN:
119       Console::instance->enter();
120       break;
121     case SDLK_BACKSPACE:
122       Console::instance->backspace();
123       break;
124     case SDLK_TAB:
125       Console::instance->autocomplete();
126       break;
127     case SDLK_PAGEUP:
128       Console::instance->scroll(-1);
129       break;
130     case SDLK_PAGEDOWN:
131       Console::instance->scroll(+1);
132       break;
133     case SDLK_HOME:
134       Console::instance->move_cursor(-65535);
135       break;
136     case SDLK_END:
137       Console::instance->move_cursor(+65535);
138       break;
139     case SDLK_UP:
140       Console::instance->show_history(-1);
141       break;
142     case SDLK_DOWN:
143       Console::instance->show_history(+1);
144       break;
145     case SDLK_LEFT:
146       Console::instance->move_cursor(-1);
147       break;
148     case SDLK_RIGHT:
149       Console::instance->move_cursor(+1);
150       break;
151     default:
152       break;
153   }
154 }
155
156 void
157 KeyboardManager::process_menu_key_event(const SDL_KeyboardEvent& event)
158 {
159   // wait for key mode?
160   if (wait_for_key >= 0) 
161   {
162     if (event.type == SDL_KEYUP)
163       return;
164
165     if (event.keysym.sym != SDLK_ESCAPE && 
166         event.keysym.sym != SDLK_PAUSE) 
167     {
168       bind_key(event.keysym.sym, static_cast<Controller::Control>(wait_for_key));
169     }
170     m_parent->reset();
171     MenuManager::instance().refresh();
172     wait_for_key = -1;
173     return;
174   }
175   
176   if (m_parent->joystick_manager->wait_for_joystick >= 0) 
177   {
178     if (event.keysym.sym == SDLK_ESCAPE) 
179     {
180       m_parent->reset();
181       MenuManager::instance().refresh();
182       m_parent->joystick_manager->wait_for_joystick = -1;
183     }
184     return;
185   }
186
187   Controller::Control control;
188   /* we use default keys when the menu is open (to avoid problems when
189    * redefining keys to invalid settings
190    */
191   switch(event.keysym.sym) {
192     case SDLK_UP:
193       control = Controller::UP;
194       break;
195     case SDLK_DOWN:
196       control = Controller::DOWN;
197       break;
198     case SDLK_LEFT:
199       control = Controller::LEFT;
200       break;
201     case SDLK_RIGHT:
202       control = Controller::RIGHT;
203       break;
204     case SDLK_SPACE:
205     case SDLK_RETURN:
206     case SDLK_KP_ENTER:
207       control = Controller::MENU_SELECT;
208       break;
209     case SDLK_ESCAPE:
210     case SDLK_PAUSE:
211       control = Controller::PAUSE_MENU;
212       break;
213     default:
214       return;
215       break;
216   }
217
218   m_parent->get_controller()->set_control(control, (event.type == SDL_KEYDOWN));
219 }
220
221 void
222 KeyboardManager::bind_key(SDL_Keycode key, Controller::Control control)
223 {
224   // remove all previous mappings for that control and for that key
225   for(KeyMap::iterator i = keymap.begin();
226       i != keymap.end(); /* no ++i */) {
227     if (i->second == control) {
228       KeyMap::iterator e = i;
229       ++i;
230       keymap.erase(e);
231     } else {
232       ++i;
233     }
234   }
235
236   KeyMap::iterator i = keymap.find(key);
237   if (i != keymap.end())
238     keymap.erase(i);
239
240   // add new mapping
241   keymap[key] = control;
242 }
243
244 SDL_Keycode
245 KeyboardManager::reversemap_key(Controller::Control c)
246 {
247   for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) 
248   {
249     if (i->second == c)
250     {
251       return i->first;
252     }
253   }
254
255   return SDLK_UNKNOWN;
256 }
257
258 void
259 KeyboardManager::read(const lisp::Lisp* keymap_lisp)
260 {
261   // keycode values changed between SDL1 and SDL2, so we skip old SDL1
262   // based values and use the defaults instead on the first read of
263   // the config file
264   bool config_is_sdl2 = false;
265   keymap_lisp->get("sdl2", config_is_sdl2);
266   if (config_is_sdl2)
267   {
268     keymap.clear();
269     keymap_lisp->get("jump-with-up", jump_with_up_kbd);
270     lisp::ListIterator iter(keymap_lisp);
271     while(iter.next()) {
272       if (iter.item() == "map") {
273         int key = -1;
274         std::string control;
275         const lisp::Lisp* map = iter.lisp();
276         map->get("key", key);
277
278         map->get("control", control);
279
280         int i = 0;
281         for(i = 0; Controller::controlNames[i] != 0; ++i) {
282           if (control == Controller::controlNames[i])
283             break;
284         }
285         if (Controller::controlNames[i] == 0) {
286           log_info << "Invalid control '" << control << "' in keymap" << std::endl;
287           continue;
288         }
289         keymap[static_cast<SDL_Keycode>(key)] = static_cast<Controller::Control>(i);
290       }
291     }
292   }
293 }
294
295 void
296 KeyboardManager::write(Writer& writer)
297 {
298   writer.write("sdl2", true);
299   writer.write("jump-with-up", jump_with_up_kbd);
300   for(KeyMap::iterator i = keymap.begin(); i != keymap.end(); ++i) {
301     writer.start_list("map");
302     writer.write("key", (int) i->first);
303     writer.write("control", Controller::controlNames[i->second]);
304     writer.end_list("map");
305   }
306 }
307
308 /* EOF */