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
26JoystickConfig::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
49int
50JoystickConfig::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
60int
61JoystickConfig::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
71int
72JoystickConfig::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
82void
83JoystickConfig::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
101void
102JoystickConfig::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
127void
128JoystickConfig::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
141void
142JoystickConfig::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
150void
151JoystickConfig::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
159void
160JoystickConfig::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
215void
216JoystickConfig::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