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
22InputController::InputController(QObject* parent)
23 : QObject(parent)
24 , m_config(nullptr)
25 , m_gamepadTimer(nullptr)
26{
27 GBAInputMapInit(&m_inputMap);
28
29#ifdef BUILD_SDL
30 m_sdlEvents.bindings = &m_inputMap;
31 GBASDLInitEvents(&m_sdlEvents);
32 GBASDLInitBindings(&m_inputMap);
33 SDL_JoystickEventState(SDL_QUERY);
34
35 m_gamepadTimer = new QTimer(this);
36 connect(m_gamepadTimer, SIGNAL(timeout()), this, SLOT(testGamepad()));
37 m_gamepadTimer->setInterval(50);
38 m_gamepadTimer->start();
39#endif
40
41 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
42 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
43 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
44 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
45 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
46 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
47 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
48 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
49 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
50 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
51}
52
53InputController::~InputController() {
54 GBAInputMapDeinit(&m_inputMap);
55
56#ifdef BUILD_SDL
57 GBASDLDeinitEvents(&m_sdlEvents);
58#endif
59}
60
61void InputController::setConfiguration(ConfigController* config) {
62 m_config = config;
63 loadConfiguration(KEYBOARD);
64#ifdef BUILD_SDL
65 loadConfiguration(SDL_BINDING_BUTTON);
66 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
67#endif
68}
69
70void InputController::loadConfiguration(uint32_t type) {
71 GBAInputMapLoad(&m_inputMap, type, m_config->configuration());
72}
73
74void InputController::loadProfile(uint32_t type, const char* profile) {
75 GBAInputProfileLoad(&m_inputMap, type, m_config->configuration(), profile);
76}
77
78void InputController::saveConfiguration(uint32_t type) {
79 GBAInputMapSave(&m_inputMap, type, m_config->configuration());
80 m_config->write();
81}
82
83void InputController::saveProfile(uint32_t type, const char* profile) {
84 GBAInputProfileSave(&m_inputMap, type, m_config->configuration(), profile);
85 m_config->write();
86}
87
88const char* InputController::profileForType(uint32_t type) {
89 UNUSED(type);
90#ifdef BUILD_SDL
91 if (type == SDL_BINDING_BUTTON) {
92#if SDL_VERSION_ATLEAST(2, 0, 0)
93 return SDL_JoystickName(m_sdlEvents.joystick);
94#else
95 return SDL_JoystickName(SDL_JoystickIndex(m_sdlEvents.joystick));
96#endif
97 }
98#endif
99 return 0;
100}
101
102GBAKey InputController::mapKeyboard(int key) const {
103 return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
104}
105
106void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
107 return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
108}
109
110#ifdef BUILD_SDL
111int InputController::testSDLEvents() {
112 SDL_Joystick* joystick = m_sdlEvents.joystick;
113 SDL_JoystickUpdate();
114 int numButtons = SDL_JoystickNumButtons(joystick);
115 int activeButtons = 0;
116 int i;
117 for (i = 0; i < numButtons; ++i) {
118 GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
119 if (key == GBA_KEY_NONE) {
120 continue;
121 }
122 if (hasPendingEvent(key)) {
123 continue;
124 }
125 if (SDL_JoystickGetButton(joystick, i)) {
126 activeButtons |= 1 << key;
127 }
128 }
129 int numHats = SDL_JoystickNumHats(joystick);
130 for (i = 0; i < numHats; ++i) {
131 int hat = SDL_JoystickGetHat(joystick, i);
132 if (hat & SDL_HAT_UP) {
133 activeButtons |= 1 << GBA_KEY_UP;
134 }
135 if (hat & SDL_HAT_LEFT) {
136 activeButtons |= 1 << GBA_KEY_LEFT;
137 }
138 if (hat & SDL_HAT_DOWN) {
139 activeButtons |= 1 << GBA_KEY_DOWN;
140 }
141 if (hat & SDL_HAT_RIGHT) {
142 activeButtons |= 1 << GBA_KEY_RIGHT;
143 }
144 }
145
146 int numAxes = SDL_JoystickNumAxes(joystick);
147 for (i = 0; i < numAxes; ++i) {
148 int value = SDL_JoystickGetAxis(joystick, i);
149
150 enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
151 if (key != GBA_KEY_NONE) {
152 activeButtons |= 1 << key;
153 }
154 }
155 return activeButtons;
156}
157
158QSet<int> InputController::activeGamepadButtons() {
159 SDL_Joystick* joystick = m_sdlEvents.joystick;
160 SDL_JoystickUpdate();
161 int numButtons = SDL_JoystickNumButtons(joystick);
162 QSet<int> activeButtons;
163 int i;
164 for (i = 0; i < numButtons; ++i) {
165 if (SDL_JoystickGetButton(joystick, i)) {
166 activeButtons.insert(i);
167 }
168 }
169 return activeButtons;
170}
171
172QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes() {
173 SDL_Joystick* joystick = m_sdlEvents.joystick;
174 SDL_JoystickUpdate();
175 int numButtons = SDL_JoystickNumAxes(joystick);
176 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
177 int i;
178 for (i = 0; i < numButtons; ++i) {
179 int32_t axis = SDL_JoystickGetAxis(joystick, i);
180 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
181 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
182 }
183 }
184 return activeAxes;
185}
186
187void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
188 const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, SDL_BINDING_BUTTON, axis);
189 GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
190 if (old) {
191 description = *old;
192 }
193 switch (direction) {
194 case GamepadAxisEvent::NEGATIVE:
195 description.lowDirection = key;
196 description.deadLow = -AXIS_THRESHOLD;
197 break;
198 case GamepadAxisEvent::POSITIVE:
199 description.highDirection = key;
200 description.deadHigh = AXIS_THRESHOLD;
201 break;
202 default:
203 return;
204 }
205 GBAInputBindAxis(&m_inputMap, SDL_BINDING_BUTTON, axis, &description);
206}
207#endif
208
209void InputController::testGamepad() {
210#ifdef BUILD_SDL
211 auto activeAxes = activeGamepadAxes();
212 auto oldAxes = m_activeAxes;
213 m_activeAxes = activeAxes;
214
215 auto activeButtons = activeGamepadButtons();
216 auto oldButtons = m_activeButtons;
217 m_activeButtons = activeButtons;
218
219 if (!QApplication::focusWidget()) {
220 return;
221 }
222
223 activeAxes.subtract(oldAxes);
224 oldAxes.subtract(m_activeAxes);
225
226 for (auto& axis : m_activeAxes) {
227 bool newlyAboveThreshold = activeAxes.contains(axis);
228 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, this);
229 if (newlyAboveThreshold) {
230 postPendingEvent(event->gbaKey());
231 if (!event->isAccepted()) {
232 clearPendingEvent(event->gbaKey());
233 }
234 } else if (oldAxes.contains(axis)) {
235 clearPendingEvent(event->gbaKey());
236 }
237 QApplication::sendEvent(QApplication::focusWidget(), event);
238 }
239
240 activeButtons.subtract(oldButtons);
241 oldButtons.subtract(m_activeButtons);
242
243 for (int button : activeButtons) {
244 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, this);
245 postPendingEvent(event->gbaKey());
246 QApplication::sendEvent(QApplication::focusWidget(), event);
247 if (!event->isAccepted()) {
248 clearPendingEvent(event->gbaKey());
249 }
250 }
251 for (int button : oldButtons) {
252 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, this);
253 clearPendingEvent(event->gbaKey());
254 QApplication::sendEvent(QApplication::focusWidget(), event);
255 }
256#endif
257}
258
259void InputController::postPendingEvent(GBAKey key) {
260 m_pendingEvents.insert(key);
261}
262
263void InputController::clearPendingEvent(GBAKey key) {
264 m_pendingEvents.remove(key);
265}
266
267bool InputController::hasPendingEvent(GBAKey key) const {
268 return m_pendingEvents.contains(key);
269}