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#endif
67}
68
69void InputController::loadConfiguration(uint32_t type) {
70 GBAInputMapLoad(&m_inputMap, type, m_config->configuration());
71}
72
73void InputController::saveConfiguration(uint32_t type) {
74 GBAInputMapSave(&m_inputMap, type, m_config->configuration());
75 m_config->write();
76}
77
78GBAKey InputController::mapKeyboard(int key) const {
79 return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
80}
81
82void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
83 return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
84}
85
86#ifdef BUILD_SDL
87int InputController::testSDLEvents() {
88 SDL_Joystick* joystick = m_sdlEvents.joystick;
89 SDL_JoystickUpdate();
90 int numButtons = SDL_JoystickNumButtons(joystick);
91 int activeButtons = 0;
92 int i;
93 for (i = 0; i < numButtons; ++i) {
94 GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
95 if (key == GBA_KEY_NONE) {
96 continue;
97 }
98 if (hasPendingEvent(key)) {
99 continue;
100 }
101 if (SDL_JoystickGetButton(joystick, i)) {
102 activeButtons |= 1 << key;
103 }
104 }
105 int numHats = SDL_JoystickNumHats(joystick);
106 for (i = 0; i < numHats; ++i) {
107 int hat = SDL_JoystickGetHat(joystick, i);
108 if (hat & SDL_HAT_UP) {
109 activeButtons |= 1 << GBA_KEY_UP;
110 }
111 if (hat & SDL_HAT_LEFT) {
112 activeButtons |= 1 << GBA_KEY_LEFT;
113 }
114 if (hat & SDL_HAT_DOWN) {
115 activeButtons |= 1 << GBA_KEY_DOWN;
116 }
117 if (hat & SDL_HAT_RIGHT) {
118 activeButtons |= 1 << GBA_KEY_RIGHT;
119 }
120 }
121
122 int numAxes = SDL_JoystickNumAxes(joystick);
123 for (i = 0; i < numAxes; ++i) {
124 int value = SDL_JoystickGetAxis(joystick, i);
125
126 enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
127 if (key != GBA_KEY_NONE) {
128 activeButtons |= 1 << key;
129 }
130 }
131 return activeButtons;
132}
133
134QSet<int> InputController::activeGamepadButtons() {
135 SDL_Joystick* joystick = m_sdlEvents.joystick;
136 SDL_JoystickUpdate();
137 int numButtons = SDL_JoystickNumButtons(joystick);
138 QSet<int> activeButtons;
139 int i;
140 for (i = 0; i < numButtons; ++i) {
141 if (SDL_JoystickGetButton(joystick, i)) {
142 activeButtons.insert(i);
143 }
144 }
145 return activeButtons;
146}
147
148QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes() {
149 SDL_Joystick* joystick = m_sdlEvents.joystick;
150 SDL_JoystickUpdate();
151 int numButtons = SDL_JoystickNumAxes(joystick);
152 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
153 int i;
154 for (i = 0; i < numButtons; ++i) {
155 int32_t axis = SDL_JoystickGetAxis(joystick, i);
156 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
157 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
158 }
159 }
160 return activeAxes;
161}
162
163void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
164 const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, SDL_BINDING_BUTTON, axis);
165 GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
166 if (old) {
167 description = *old;
168 }
169 switch (direction) {
170 case GamepadAxisEvent::NEGATIVE:
171 description.lowDirection = key;
172 description.deadLow = -AXIS_THRESHOLD;
173 break;
174 case GamepadAxisEvent::POSITIVE:
175 description.highDirection = key;
176 description.deadHigh = AXIS_THRESHOLD;
177 break;
178 default:
179 return;
180 }
181 GBAInputBindAxis(&m_inputMap, SDL_BINDING_BUTTON, axis, &description);
182}
183#endif
184
185void InputController::testGamepad() {
186#ifdef BUILD_SDL
187 auto activeAxes = activeGamepadAxes();
188 auto oldAxes = m_activeAxes;
189 m_activeAxes = activeAxes;
190
191 auto activeButtons = activeGamepadButtons();
192 auto oldButtons = m_activeButtons;
193 m_activeButtons = activeButtons;
194
195 if (!QApplication::focusWidget()) {
196 return;
197 }
198
199 activeAxes.subtract(oldAxes);
200 oldAxes.subtract(m_activeAxes);
201
202 for (auto& axis : m_activeAxes) {
203 bool newlyAboveThreshold = activeAxes.contains(axis);
204 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, this);
205 if (newlyAboveThreshold) {
206 postPendingEvent(event->gbaKey());
207 if (!event->isAccepted()) {
208 clearPendingEvent(event->gbaKey());
209 }
210 } else if (oldAxes.contains(axis)) {
211 clearPendingEvent(event->gbaKey());
212 }
213 QApplication::sendEvent(QApplication::focusWidget(), event);
214 }
215
216 activeButtons.subtract(oldButtons);
217 oldButtons.subtract(m_activeButtons);
218
219 for (int button : activeButtons) {
220 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, this);
221 postPendingEvent(event->gbaKey());
222 QApplication::sendEvent(QApplication::focusWidget(), event);
223 if (!event->isAccepted()) {
224 clearPendingEvent(event->gbaKey());
225 }
226 }
227 for (int button : oldButtons) {
228 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, this);
229 clearPendingEvent(event->gbaKey());
230 QApplication::sendEvent(QApplication::focusWidget(), event);
231 }
232#endif
233}
234
235void InputController::postPendingEvent(GBAKey key) {
236 m_pendingEvents.insert(key);
237}
238
239void InputController::clearPendingEvent(GBAKey key) {
240 m_pendingEvents.remove(key);
241}
242
243bool InputController::hasPendingEvent(GBAKey key) const {
244 return m_pendingEvents.contains(key);
245}