| 1 | // SuperTux |
| 2 | // Copyright (C) 2014 Ingo Ruhnke <grumbel@gmail.com> |
| 3 | // |
| 4 | // This program is free software: you can redistribute it and/or modify |
| 5 | // it under the terms of the GNU General Public License as published by |
| 6 | // the Free Software Foundation, either version 3 of the License, or |
| 7 | // (at your option) any later version. |
| 8 | // |
| 9 | // This program is distributed in the hope that it will be useful, |
| 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | // GNU General Public License for more details. |
| 13 | // |
| 14 | // You should have received a copy of the GNU General Public License |
| 15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | |
| 17 | #include "control/joystick_config.hpp" |
| 18 | |
| 19 | #include <iostream> |
| 20 | |
| 21 | #include "util/gettext.hpp" |
| 22 | #include "util/log.hpp" |
| 23 | #include "util/reader_mapping.hpp" |
| 24 | #include "util/writer.hpp" |
| 25 | |
| 26 | JoystickConfig::JoystickConfig() : |
| 27 | m_dead_zone(8000), |
| 28 | m_jump_with_up_joy(false), |
| 29 | m_use_game_controller(true), |
| 30 | m_joy_button_map(), |
| 31 | m_joy_axis_map(), |
| 32 | m_joy_hat_map() |
| 33 | { |
| 34 | // Default joystick button configuration |
| 35 | bind_joybutton(0, 0, Control::JUMP); |
| 36 | bind_joybutton(0, 0, Control::MENU_SELECT); |
| 37 | bind_joybutton(0, 1, Control::ACTION); |
| 38 | bind_joybutton(0, 4, Control::PEEK_LEFT); |
| 39 | bind_joybutton(0, 5, Control::PEEK_RIGHT); |
| 40 | bind_joybutton(0, 6, Control::START); |
| 41 | |
| 42 | // Default joystick axis configuration |
| 43 | bind_joyaxis(0, -1, Control::LEFT); |
| 44 | bind_joyaxis(0, 1, Control::RIGHT); |
| 45 | bind_joyaxis(0, -2, Control::UP); |
| 46 | bind_joyaxis(0, 2, Control::DOWN); |
| 47 | } |
| 48 | |
| 49 | int |
| 50 | JoystickConfig::reversemap_joyaxis(Control c) const |
| 51 | { |
| 52 | for (const auto& i : m_joy_axis_map) { |
| 53 | if (i.second == c) |
| 54 | return i.first.second; |
| 55 | } |
| 56 | |
| 57 | return 0; |
| 58 | } |
| 59 | |
| 60 | int |
| 61 | JoystickConfig::reversemap_joybutton(Control c) const |
| 62 | { |
| 63 | for (const auto& i : m_joy_button_map) { |
| 64 | if (i.second == c) |
| 65 | return i.first.second; |
| 66 | } |
| 67 | |
| 68 | return -1; |
| 69 | } |
| 70 | |
| 71 | int |
| 72 | JoystickConfig::reversemap_joyhat(Control c) const |
| 73 | { |
| 74 | for (const auto& i : m_joy_hat_map) { |
| 75 | if (i.second == c) |
| 76 | return i.first.second; |
| 77 | } |
| 78 | |
| 79 | return -1; |
| 80 | } |
| 81 | |
| 82 | void |
| 83 | JoystickConfig::print_joystick_mappings() const |
| 84 | { |
| 85 | std::cout << _("Joystick Mappings" ) << std::endl; |
| 86 | std::cout << "-----------------" << std::endl; |
| 87 | for (const auto& i : m_joy_axis_map) { |
| 88 | std::cout << "Axis: " << i.first.second << " -> " << i.second << std::endl; |
| 89 | } |
| 90 | |
| 91 | for (const auto& i : m_joy_button_map) { |
| 92 | std::cout << "Button: " << i.first.second << " -> " << i.second << std::endl; |
| 93 | } |
| 94 | |
| 95 | for (const auto& i : m_joy_hat_map) { |
| 96 | std::cout << "Hat: " << i.first.second << " -> " << i.second << std::endl; |
| 97 | } |
| 98 | std::cout << std::endl; |
| 99 | } |
| 100 | |
| 101 | void |
| 102 | JoystickConfig::unbind_joystick_control(Control control) |
| 103 | { |
| 104 | // remove all previous mappings for that control |
| 105 | for (auto i = m_joy_axis_map.begin(); i != m_joy_axis_map.end(); /* no ++i */) { |
| 106 | if (i->second == control) |
| 107 | m_joy_axis_map.erase(i++); |
| 108 | else |
| 109 | ++i; |
| 110 | } |
| 111 | |
| 112 | for (auto i = m_joy_button_map.begin(); i != m_joy_button_map.end(); /* no ++i */) { |
| 113 | if (i->second == control) |
| 114 | m_joy_button_map.erase(i++); |
| 115 | else |
| 116 | ++i; |
| 117 | } |
| 118 | |
| 119 | for (auto i = m_joy_hat_map.begin(); i != m_joy_hat_map.end(); /* no ++i */) { |
| 120 | if (i->second == control) |
| 121 | m_joy_hat_map.erase(i++); |
| 122 | else |
| 123 | ++i; |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | void |
| 128 | JoystickConfig::bind_joyaxis(JoystickID joy_id, int axis, Control control) |
| 129 | { |
| 130 | // axis isn't the SDL axis number, but axisnumber + 1 with sign |
| 131 | // changed depending on if the positive or negative end is to be |
| 132 | // used (negative axis 0 becomes -1, positive axis 2 becomes +3, |
| 133 | // etc.) |
| 134 | |
| 135 | unbind_joystick_control(control); |
| 136 | |
| 137 | // add new mapping |
| 138 | m_joy_axis_map[std::make_pair(joy_id, axis)] = control; |
| 139 | } |
| 140 | |
| 141 | void |
| 142 | JoystickConfig::bind_joyhat(JoystickID joy_id, int dir, Control c) |
| 143 | { |
| 144 | unbind_joystick_control(c); |
| 145 | |
| 146 | // add new mapping |
| 147 | m_joy_hat_map[std::make_pair(joy_id, dir)] = c; |
| 148 | } |
| 149 | |
| 150 | void |
| 151 | JoystickConfig::bind_joybutton(JoystickID joy_id, int button, Control control) |
| 152 | { |
| 153 | unbind_joystick_control(control); |
| 154 | |
| 155 | // add new mapping |
| 156 | m_joy_button_map[std::make_pair(joy_id, button)] = control; |
| 157 | } |
| 158 | |
| 159 | void |
| 160 | JoystickConfig::read(const ReaderMapping& joystick_mapping) |
| 161 | { |
| 162 | joystick_mapping.get("dead-zone" , m_dead_zone); |
| 163 | joystick_mapping.get("jump-with-up" , m_jump_with_up_joy); |
| 164 | joystick_mapping.get("use-game-controller" , m_use_game_controller); |
| 165 | |
| 166 | auto iter = joystick_mapping.get_iter(); |
| 167 | while (iter.next()) |
| 168 | { |
| 169 | if (iter.get_key() == "map" ) |
| 170 | { |
| 171 | const auto& map = iter.as_mapping(); |
| 172 | |
| 173 | std::string control_text; |
| 174 | map.get("control" , control_text); |
| 175 | |
| 176 | const boost::optional<Control> maybe_control = Control_from_string(control_text); |
| 177 | if (!maybe_control) |
| 178 | { |
| 179 | log_info << "Invalid control '" << control_text << "' in buttonmap" << std::endl; |
| 180 | } |
| 181 | else |
| 182 | { |
| 183 | const Control control = *maybe_control; |
| 184 | |
| 185 | int button = -1; |
| 186 | int axis = 0; |
| 187 | int hat = -1; |
| 188 | |
| 189 | if (map.get("button" , button)) |
| 190 | { |
| 191 | bind_joybutton(0, button, control); |
| 192 | } |
| 193 | else if (map.get("axis" , axis)) |
| 194 | { |
| 195 | bind_joyaxis(0, axis, control); |
| 196 | } |
| 197 | else if (map.get("hat" , hat)) |
| 198 | { |
| 199 | if (hat != SDL_HAT_UP && |
| 200 | hat != SDL_HAT_DOWN && |
| 201 | hat != SDL_HAT_LEFT && |
| 202 | hat != SDL_HAT_RIGHT) { |
| 203 | log_info << "Invalid axis '" << axis << "' in axismap" << std::endl; |
| 204 | } |
| 205 | else |
| 206 | { |
| 207 | bind_joyhat(0, hat, control); |
| 208 | } |
| 209 | } |
| 210 | } |
| 211 | } |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | void |
| 216 | JoystickConfig::write(Writer& writer) |
| 217 | { |
| 218 | writer.write("dead-zone" , m_dead_zone); |
| 219 | writer.write("jump-with-up" , m_jump_with_up_joy); |
| 220 | writer.write("use-game-controller" , m_use_game_controller); |
| 221 | |
| 222 | for (const auto& i : m_joy_button_map) { |
| 223 | writer.start_list("map" ); |
| 224 | writer.write("button" , i.first.second); |
| 225 | writer.write("control" , Control_to_string(i.second)); |
| 226 | writer.end_list("map" ); |
| 227 | } |
| 228 | |
| 229 | for (const auto& i : m_joy_hat_map) { |
| 230 | writer.start_list("map" ); |
| 231 | writer.write("hat" , i.first.second); |
| 232 | writer.write("control" , Control_to_string(i.second)); |
| 233 | writer.end_list("map" ); |
| 234 | } |
| 235 | |
| 236 | for (const auto& i : m_joy_axis_map) { |
| 237 | writer.start_list("map" ); |
| 238 | writer.write("axis" , i.first.second); |
| 239 | writer.write("control" , Control_to_string(i.second)); |
| 240 | writer.end_list("map" ); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | /* EOF */ |
| 245 | |