src/platform/qt/InputController.cpp (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "InputController.h"
7
8#include "ConfigController.h"
9#include "GamepadAxisEvent.h"
10#include "GamepadButtonEvent.h"
11
12#include <QApplication>
13#include <QTimer>
14#include <QWidget>
15
16extern "C" {
17#include "util/configuration.h"
18}
19
20using namespace QGBA;
21
22#ifdef BUILD_SDL
23int InputController::s_sdlInited = 0;
24GBASDLEvents InputController::s_sdlEvents;
25#endif
26
27InputController::InputController(int playerId, QObject* parent)
28 : QObject(parent)
29 , m_playerId(playerId)
30 , m_config(nullptr)
31 , m_gamepadTimer(nullptr)
32#ifdef BUILD_SDL
33 , m_playerAttached(false)
34#endif
35 , m_allowOpposing(false)
36{
37 GBAInputMapInit(&m_inputMap);
38
39#ifdef BUILD_SDL
40 if (s_sdlInited == 0) {
41 GBASDLInitEvents(&s_sdlEvents);
42 }
43 ++s_sdlInited;
44 m_sdlPlayer.bindings = &m_inputMap;
45 GBASDLInitBindings(&m_inputMap);
46
47 m_gamepadTimer = new QTimer(this);
48 connect(m_gamepadTimer, SIGNAL(timeout()), this, SLOT(testGamepad()));
49 m_gamepadTimer->setInterval(50);
50 m_gamepadTimer->start();
51#endif
52
53 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
54 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
55 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
56 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
57 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
58 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
59 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
60 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
61 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
62 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
63}
64
65InputController::~InputController() {
66 GBAInputMapDeinit(&m_inputMap);
67
68#ifdef BUILD_SDL
69 if (m_playerAttached) {
70 GBASDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
71 }
72
73 --s_sdlInited;
74 if (s_sdlInited == 0) {
75 GBASDLDeinitEvents(&s_sdlEvents);
76 }
77#endif
78}
79
80void InputController::setConfiguration(ConfigController* config) {
81 m_config = config;
82 setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
83 loadConfiguration(KEYBOARD);
84#ifdef BUILD_SDL
85 GBASDLEventsLoadConfig(&s_sdlEvents, config->input());
86 if (!m_playerAttached) {
87 GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
88 m_playerAttached = true;
89 }
90 loadConfiguration(SDL_BINDING_BUTTON);
91 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
92#endif
93}
94
95void InputController::loadConfiguration(uint32_t type) {
96 GBAInputMapLoad(&m_inputMap, type, m_config->input());
97}
98
99void InputController::loadProfile(uint32_t type, const QString& profile) {
100 GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
101#ifdef BUILD_SDL
102 recalibrateAxes();
103#endif
104}
105
106void InputController::saveConfiguration(uint32_t type) {
107 GBAInputMapSave(&m_inputMap, type, m_config->input());
108 m_config->write();
109}
110
111void InputController::saveProfile(uint32_t type, const QString& profile) {
112 GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
113 m_config->write();
114}
115
116const char* InputController::profileForType(uint32_t type) {
117 UNUSED(type);
118#ifdef BUILD_SDL
119 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
120#if SDL_VERSION_ATLEAST(2, 0, 0)
121 return SDL_JoystickName(m_sdlPlayer.joystick);
122#else
123 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick));
124#endif
125 }
126#endif
127 return 0;
128}
129
130#ifdef BUILD_SDL
131QStringList InputController::connectedGamepads(uint32_t type) const {
132 UNUSED(type);
133 if (type != SDL_BINDING_BUTTON) {
134 return QStringList();
135 }
136
137 QStringList pads;
138 for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) {
139 const char* name;
140#if SDL_VERSION_ATLEAST(2, 0, 0)
141 name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
142#else
143 name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
144#endif
145 if (name) {
146 pads.append(QString(name));
147 } else {
148 pads.append(QString());
149 }
150 }
151 return pads;
152}
153
154void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
155 if (!m_config) {
156 return;
157 }
158 GBAInputSetPreferredDevice(m_config->input(), type, m_sdlPlayer.playerId, device.toLocal8Bit().constData());
159}
160
161GBARumble* InputController::rumble() {
162 return &m_sdlPlayer.rumble.d;
163}
164
165GBARotationSource* InputController::rotationSource() {
166 return &m_sdlPlayer.rotation.d;
167}
168#else
169GBARumble* InputController::rumble() {
170 return nullptr;
171}
172
173GBARotationSource* InputController::rotationSource() {
174 return nullptr;
175}
176#endif
177
178GBAKey InputController::mapKeyboard(int key) const {
179 return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
180}
181
182void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
183 return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
184}
185
186#ifdef BUILD_SDL
187int InputController::testSDLEvents() {
188 SDL_Joystick* joystick = m_sdlPlayer.joystick;
189 SDL_JoystickUpdate();
190 int numButtons = SDL_JoystickNumButtons(joystick);
191 int activeButtons = 0;
192 int i;
193 for (i = 0; i < numButtons; ++i) {
194 GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
195 if (key == GBA_KEY_NONE) {
196 continue;
197 }
198 if (hasPendingEvent(key)) {
199 continue;
200 }
201 if (SDL_JoystickGetButton(joystick, i)) {
202 activeButtons |= 1 << key;
203 }
204 }
205 int numHats = SDL_JoystickNumHats(joystick);
206 for (i = 0; i < numHats; ++i) {
207 int hat = SDL_JoystickGetHat(joystick, i);
208 if (hat & SDL_HAT_UP) {
209 activeButtons |= 1 << GBA_KEY_UP;
210 }
211 if (hat & SDL_HAT_LEFT) {
212 activeButtons |= 1 << GBA_KEY_LEFT;
213 }
214 if (hat & SDL_HAT_DOWN) {
215 activeButtons |= 1 << GBA_KEY_DOWN;
216 }
217 if (hat & SDL_HAT_RIGHT) {
218 activeButtons |= 1 << GBA_KEY_RIGHT;
219 }
220 }
221
222 int numAxes = SDL_JoystickNumAxes(joystick);
223 for (i = 0; i < numAxes; ++i) {
224 int value = SDL_JoystickGetAxis(joystick, i);
225
226 enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
227 if (key != GBA_KEY_NONE) {
228 activeButtons |= 1 << key;
229 }
230 }
231 return activeButtons;
232}
233
234QSet<int> InputController::activeGamepadButtons() {
235 SDL_Joystick* joystick = m_sdlPlayer.joystick;
236 SDL_JoystickUpdate();
237 int numButtons = SDL_JoystickNumButtons(joystick);
238 QSet<int> activeButtons;
239 int i;
240 for (i = 0; i < numButtons; ++i) {
241 if (SDL_JoystickGetButton(joystick, i)) {
242 activeButtons.insert(i);
243 }
244 }
245 return activeButtons;
246}
247
248void InputController::recalibrateAxes() {
249 SDL_Joystick* joystick = m_sdlPlayer.joystick;
250 SDL_JoystickUpdate();
251 int numAxes = SDL_JoystickNumAxes(joystick);
252 if (numAxes < 1) {
253 return;
254 }
255 m_deadzones.resize(numAxes);
256 int i;
257 for (i = 0; i < numAxes; ++i) {
258 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
259 }
260}
261
262QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes() {
263 SDL_Joystick* joystick = m_sdlPlayer.joystick;
264 SDL_JoystickUpdate();
265 int numAxes = SDL_JoystickNumAxes(joystick);
266 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
267 if (numAxes < 1) {
268 return activeAxes;
269 }
270 m_deadzones.resize(numAxes);
271 int i;
272 for (i = 0; i < numAxes; ++i) {
273 int32_t axis = SDL_JoystickGetAxis(joystick, i);
274 axis -= m_deadzones[i];
275 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
276 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
277 }
278 }
279 return activeAxes;
280}
281
282void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
283 const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, SDL_BINDING_BUTTON, axis);
284 GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
285 if (old) {
286 description = *old;
287 }
288 switch (direction) {
289 case GamepadAxisEvent::NEGATIVE:
290 description.lowDirection = key;
291 description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
292 break;
293 case GamepadAxisEvent::POSITIVE:
294 description.highDirection = key;
295 description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
296 break;
297 default:
298 return;
299 }
300 GBAInputBindAxis(&m_inputMap, SDL_BINDING_BUTTON, axis, &description);
301}
302#endif
303
304void InputController::testGamepad() {
305#ifdef BUILD_SDL
306 auto activeAxes = activeGamepadAxes();
307 auto oldAxes = m_activeAxes;
308 m_activeAxes = activeAxes;
309
310 auto activeButtons = activeGamepadButtons();
311 auto oldButtons = m_activeButtons;
312 m_activeButtons = activeButtons;
313
314 if (!QApplication::focusWidget()) {
315 return;
316 }
317
318 activeAxes.subtract(oldAxes);
319 oldAxes.subtract(m_activeAxes);
320
321 for (auto& axis : m_activeAxes) {
322 bool newlyAboveThreshold = activeAxes.contains(axis);
323 if (newlyAboveThreshold) {
324 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, this);
325 postPendingEvent(event->gbaKey());
326 QApplication::sendEvent(QApplication::focusWidget(), event);
327 if (!event->isAccepted()) {
328 clearPendingEvent(event->gbaKey());
329 }
330 }
331 }
332 for (auto axis : oldAxes) {
333 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, this);
334 clearPendingEvent(event->gbaKey());
335 QApplication::sendEvent(QApplication::focusWidget(), event);
336 }
337
338 if (!QApplication::focusWidget()) {
339 return;
340 }
341
342 activeButtons.subtract(oldButtons);
343 oldButtons.subtract(m_activeButtons);
344
345 for (int button : activeButtons) {
346 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, this);
347 postPendingEvent(event->gbaKey());
348 QApplication::sendEvent(QApplication::focusWidget(), event);
349 if (!event->isAccepted()) {
350 clearPendingEvent(event->gbaKey());
351 }
352 }
353 for (int button : oldButtons) {
354 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, this);
355 clearPendingEvent(event->gbaKey());
356 QApplication::sendEvent(QApplication::focusWidget(), event);
357 }
358#endif
359}
360
361void InputController::postPendingEvent(GBAKey key) {
362 m_pendingEvents.insert(key);
363}
364
365void InputController::clearPendingEvent(GBAKey key) {
366 m_pendingEvents.remove(key);
367}
368
369bool InputController::hasPendingEvent(GBAKey key) const {
370 return m_pendingEvents.contains(key);
371}