1 /*
2 JOYSTICK_SDL.CPP
3
4 Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 and the "Aleph One" developers.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 This license is contained in the file "COPYING",
18 which is included with this source code; it is available online at
19 http://www.gnu.org/licenses/gpl.html
20
21 May 18, 2009 (Eric Peterson):
22 Initial revision.
23
24 */
25
26 #include <SDL.h>
27 #include <boost/ptr_container/ptr_map.hpp>
28
29 #include "player.h" // for mask_in_absolute_positioning_information
30 #include "preferences.h"
31 #include "joystick.h"
32 #include "Logging.h"
33 #include "FileHandler.h"
34
35 // internal handles
36 int joystick_active = true;
37 static boost::ptr_map<int, SDL_GameController*> active_instances;
38 int axis_values[SDL_CONTROLLER_AXIS_MAX] = {};
39 bool button_values[NUM_SDL_JOYSTICK_BUTTONS] = {};
40
initialize_joystick(void)41 void initialize_joystick(void) {
42 // Look for "gamecontrollerdb.txt" in default search path
43 FileSpecifier fs;
44 if (fs.SetNameWithPath("gamecontrollerdb.txt")) {
45 SDL_GameControllerAddMappingsFromFile(fs.GetPath());
46 }
47
48 SDL_GameControllerEventState(SDL_ENABLE);
49 for (int i = 0; i < SDL_NumJoysticks(); ++i)
50 joystick_added(i);
51 }
52
enter_joystick(void)53 void enter_joystick(void) {
54 joystick_active = true;
55 }
56
exit_joystick(void)57 void exit_joystick(void) {
58 joystick_active = false;
59 }
60
joystick_added(int device_index)61 void joystick_added(int device_index) {
62 if (!SDL_IsGameController(device_index)) {
63 SDL_Joystick *joystick = SDL_JoystickOpen(device_index);
64 char guidStr[255] = "";
65 SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), guidStr, 255);
66 logWarning("No mapping found for controller \"%s\" (%s)",
67 SDL_JoystickName(joystick), guidStr);
68 return;
69 }
70 SDL_GameController *controller = SDL_GameControllerOpen(device_index);
71 if (!controller)
72 return;
73 int instance_id = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller));
74 active_instances[instance_id] = controller;
75 }
76
joystick_removed(int instance_id)77 void joystick_removed(int instance_id) {
78 SDL_GameController *controller = active_instances[instance_id];
79 if (controller) {
80 SDL_GameControllerClose(controller);
81 active_instances.erase(instance_id);
82 }
83 }
84
joystick_axis_moved(int instance_id,int axis,int value)85 void joystick_axis_moved(int instance_id, int axis, int value) {
86 switch (axis) {
87 case SDL_CONTROLLER_AXIS_LEFTY:
88 case SDL_CONTROLLER_AXIS_RIGHTY:
89 // flip Y axes to better match default movement
90 axis_values[axis] = value * -1;
91 break;
92 default:
93 axis_values[axis] = value;
94 break;
95 }
96 button_values[AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE - AO_SCANCODE_BASE_JOYSTICK_BUTTON + axis] = (value >= 16384);
97 button_values[AO_SCANCODE_BASE_JOYSTICK_AXIS_NEGATIVE - AO_SCANCODE_BASE_JOYSTICK_BUTTON + axis] = (value <= -16384);
98 }
joystick_button_pressed(int instance_id,int button,bool down)99 void joystick_button_pressed(int instance_id, int button, bool down) {
100 if (button >= 0 && button < NUM_SDL_JOYSTICK_BUTTONS)
101 button_values[button] = down;
102 }
103
104 enum {
105 _flags_yaw,
106 _flags_pitch,
107 NUMBER_OF_ABSOLUTE_POSITION_VALUES
108 };
109
110 typedef struct AxisInfo {
111 int key_binding_index;
112 int abs_pos_index;
113 bool negative;
114
115 } AxisInfo;
116
117 static const std::vector<AxisInfo> axis_mappings = {
118 { 2, _flags_yaw, true },
119 { 3, _flags_yaw, false },
120 { 8, _flags_pitch, true },
121 { 9, _flags_pitch, false }
122 };
123
124 static const float axis_limits[NUMBER_OF_ABSOLUTE_POSITION_VALUES] = {
125 0.5f - 1.f / (1<<ABSOLUTE_YAW_BITS),
126 0.5f - 1.f / (1<<ABSOLUTE_PITCH_BITS)
127 };
128
axis_mapped_to_action(int action,bool * negative)129 static int axis_mapped_to_action(int action, bool* negative) {
130 auto codeset = input_preferences->key_bindings[action];
131 for (auto it = codeset.begin(); it != codeset.end(); ++it) {
132 const SDL_Scancode code = *it;
133
134 if (code < AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE)
135 continue;
136 if (code > (AO_SCANCODE_BASE_JOYSTICK_BUTTON + NUM_SDL_JOYSTICK_BUTTONS))
137 continue;
138
139 *negative = false;
140 int axis = code - AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE;
141 if (code >= AO_SCANCODE_BASE_JOYSTICK_AXIS_NEGATIVE) {
142 *negative = true;
143 axis = code - AO_SCANCODE_BASE_JOYSTICK_AXIS_NEGATIVE;
144 }
145 return axis;
146 }
147 return -1;
148 }
149
joystick_buttons_become_keypresses(Uint8 * ioKeyMap)150 void joystick_buttons_become_keypresses(Uint8* ioKeyMap) {
151 // if we're not using the joystick, avoid this
152 if (!joystick_active)
153 return;
154 if (active_instances.empty())
155 return;
156
157 std::set<int> buttons_to_avoid;
158 if (input_preferences->controller_analog) {
159 // avoid setting buttons mapped to analog aiming
160 for (auto it = axis_mappings.begin(); it != axis_mappings.end(); ++it) {
161 const AxisInfo info = *it;
162 bool negative = false;
163 int axis = axis_mapped_to_action(info.key_binding_index, &negative);
164 if (axis >= 0) {
165 buttons_to_avoid.insert(axis + (negative ? AO_SCANCODE_BASE_JOYSTICK_AXIS_NEGATIVE : AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE));
166 }
167 }
168 }
169
170 for (int i = 0; i < NUM_SDL_JOYSTICK_BUTTONS; ++i) {
171 int code = AO_SCANCODE_BASE_JOYSTICK_BUTTON + i;
172 if (buttons_to_avoid.count(code) == 0)
173 ioKeyMap[code] = button_values[i];
174 }
175
176 return;
177 }
178
process_joystick_axes(int flags,int tick)179 int process_joystick_axes(int flags, int tick) {
180 if (!joystick_active)
181 return flags;
182 if (active_instances.empty())
183 return flags;
184 if (!input_preferences->controller_analog)
185 return flags;
186
187 float angular_deltas[NUMBER_OF_ABSOLUTE_POSITION_VALUES] = { 0, 0 };
188 for (auto it = axis_mappings.begin(); it != axis_mappings.end(); ++it) {
189 const AxisInfo info = *it;
190 bool negative = false;
191 int axis = axis_mapped_to_action(info.key_binding_index, &negative);
192 if (axis < 0)
193 continue;
194
195 int val = axis_values[axis] * (negative ? -1 : 1);
196 if (val > input_preferences->controller_deadzone) {
197 float norm = val/32767.f * (static_cast<float>(input_preferences->controller_sensitivity) / FIXED_ONE);
198 const float angle_per_norm = 768/63.f;
199 angular_deltas[info.abs_pos_index] += norm * (info.negative ? -1.0 : 1.0) * angle_per_norm;
200 }
201 }
202
203 // return this tick's action flags augmented with movement data
204 const fixed_angle dyaw = static_cast<fixed_angle>(angular_deltas[_flags_yaw] * FIXED_ONE);
205 const fixed_angle dpitch = static_cast<fixed_angle>(angular_deltas[_flags_pitch] * FIXED_ONE);
206 if (dyaw != 0 || dpitch != 0)
207 flags = process_aim_input(flags, {dyaw, dpitch});
208 return flags;
209 }
210