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#endif
47
48 m_gamepadTimer = new QTimer(this);
49#ifdef BUILD_SDL
50 connect(m_gamepadTimer, &QTimer::timeout, [this]() {
51 testGamepad(SDL_BINDING_BUTTON);
52 });
53#endif
54 m_gamepadTimer->setInterval(50);
55 m_gamepadTimer->start();
56
57 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
58 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
59 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
60 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
61 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
62 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
63 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
64 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
65 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
66 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
67}
68
69InputController::~InputController() {
70 GBAInputMapDeinit(&m_inputMap);
71
72#ifdef BUILD_SDL
73 if (m_playerAttached) {
74 GBASDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
75 }
76
77 --s_sdlInited;
78 if (s_sdlInited == 0) {
79 GBASDLDeinitEvents(&s_sdlEvents);
80 }
81#endif
82}
83
84void InputController::setConfiguration(ConfigController* config) {
85 m_config = config;
86 setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
87 loadConfiguration(KEYBOARD);
88#ifdef BUILD_SDL
89 GBASDLEventsLoadConfig(&s_sdlEvents, config->input());
90 if (!m_playerAttached) {
91 GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
92 m_playerAttached = true;
93 }
94 loadConfiguration(SDL_BINDING_BUTTON);
95 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
96#endif
97}
98
99void InputController::loadConfiguration(uint32_t type) {
100 GBAInputMapLoad(&m_inputMap, type, m_config->input());
101#ifdef BUILD_SDL
102 if (m_playerAttached) {
103 GBASDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
104 }
105#endif
106}
107
108void InputController::loadProfile(uint32_t type, const QString& profile) {
109 GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
110 recalibrateAxes();
111}
112
113void InputController::saveConfiguration() {
114 saveConfiguration(KEYBOARD);
115#ifdef BUILD_SDL
116 saveConfiguration(SDL_BINDING_BUTTON);
117 saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
118 if (m_playerAttached) {
119 GBASDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
120 }
121 m_config->write();
122#endif
123}
124
125void InputController::saveConfiguration(uint32_t type) {
126 GBAInputMapSave(&m_inputMap, type, m_config->input());
127 m_config->write();
128}
129
130void InputController::saveProfile(uint32_t type, const QString& profile) {
131 GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
132 m_config->write();
133}
134
135const char* InputController::profileForType(uint32_t type) {
136 UNUSED(type);
137#ifdef BUILD_SDL
138 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
139#if SDL_VERSION_ATLEAST(2, 0, 0)
140 return SDL_JoystickName(m_sdlPlayer.joystick);
141#else
142 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick));
143#endif
144 }
145#endif
146 return 0;
147}
148
149QStringList InputController::connectedGamepads(uint32_t type) const {
150 UNUSED(type);
151
152#ifdef BUILD_SDL
153 if (type == SDL_BINDING_BUTTON) {
154 QStringList pads;
155 for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) {
156 const char* name;
157#if SDL_VERSION_ATLEAST(2, 0, 0)
158 name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
159#else
160 name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
161#endif
162 if (name) {
163 pads.append(QString(name));
164 } else {
165 pads.append(QString());
166 }
167 }
168 return pads;
169 }
170#endif
171
172 return QStringList();
173}
174
175int InputController::gamepad(uint32_t type) const {
176#ifdef BUILD_SDL
177 if (type == SDL_BINDING_BUTTON) {
178 return m_sdlPlayer.joystickIndex;
179 }
180#endif
181 return 0;
182}
183
184void InputController::setGamepad(uint32_t type, int index) {
185#ifdef BUILD_SDL
186 if (type == SDL_BINDING_BUTTON) {
187 GBASDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
188 }
189#endif
190}
191
192void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
193 if (!m_config) {
194 return;
195 }
196 GBAInputSetPreferredDevice(m_config->input(), type, m_playerId, device.toLocal8Bit().constData());
197}
198
199GBARumble* InputController::rumble() {
200#ifdef BUILD_SDL
201 if (m_playerAttached) {
202 return &m_sdlPlayer.rumble.d;
203 }
204#endif
205 return nullptr;
206}
207
208GBARotationSource* InputController::rotationSource() {
209#ifdef BUILD_SDL
210 if (m_playerAttached) {
211 return &m_sdlPlayer.rotation.d;
212 }
213#endif
214 return nullptr;
215}
216
217void InputController::registerTiltAxisX(int axis) {
218#ifdef BUILD_SDL
219 if (m_playerAttached) {
220 m_sdlPlayer.rotation.axisX = axis;
221 }
222#endif
223}
224
225void InputController::registerTiltAxisY(int axis) {
226#ifdef BUILD_SDL
227 if (m_playerAttached) {
228 m_sdlPlayer.rotation.axisY = axis;
229 }
230#endif
231}
232
233void InputController::registerGyroAxisX(int axis) {
234#ifdef BUILD_SDL
235 if (m_playerAttached) {
236 m_sdlPlayer.rotation.gyroX = axis;
237 }
238#endif
239}
240
241void InputController::registerGyroAxisY(int axis) {
242#ifdef BUILD_SDL
243 if (m_playerAttached) {
244 m_sdlPlayer.rotation.gyroY = axis;
245 }
246#endif
247}
248
249float InputController::gyroSensitivity() const {
250#ifdef BUILD_SDL
251 if (m_playerAttached) {
252 return m_sdlPlayer.rotation.gyroSensitivity;
253 }
254#endif
255 return 0;
256}
257
258void InputController::setGyroSensitivity(float sensitivity) {
259#ifdef BUILD_SDL
260 if (m_playerAttached) {
261 m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
262 }
263#endif
264}
265
266GBAKey InputController::mapKeyboard(int key) const {
267 return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
268}
269
270void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
271 return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
272}
273
274int InputController::pollEvents() {
275 int activeButtons = 0;
276#ifdef BUILD_SDL
277 if (m_playerAttached) {
278 SDL_Joystick* joystick = m_sdlPlayer.joystick;
279 SDL_JoystickUpdate();
280 int numButtons = SDL_JoystickNumButtons(joystick);
281 int i;
282 for (i = 0; i < numButtons; ++i) {
283 GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
284 if (key == GBA_KEY_NONE) {
285 continue;
286 }
287 if (hasPendingEvent(key)) {
288 continue;
289 }
290 if (SDL_JoystickGetButton(joystick, i)) {
291 activeButtons |= 1 << key;
292 }
293 }
294 int numHats = SDL_JoystickNumHats(joystick);
295 for (i = 0; i < numHats; ++i) {
296 int hat = SDL_JoystickGetHat(joystick, i);
297 if (hat & SDL_HAT_UP) {
298 activeButtons |= 1 << GBA_KEY_UP;
299 }
300 if (hat & SDL_HAT_LEFT) {
301 activeButtons |= 1 << GBA_KEY_LEFT;
302 }
303 if (hat & SDL_HAT_DOWN) {
304 activeButtons |= 1 << GBA_KEY_DOWN;
305 }
306 if (hat & SDL_HAT_RIGHT) {
307 activeButtons |= 1 << GBA_KEY_RIGHT;
308 }
309 }
310
311 int numAxes = SDL_JoystickNumAxes(joystick);
312 for (i = 0; i < numAxes; ++i) {
313 int value = SDL_JoystickGetAxis(joystick, i);
314
315 enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
316 if (key != GBA_KEY_NONE) {
317 activeButtons |= 1 << key;
318 }
319 }
320 }
321#endif
322 return activeButtons;
323}
324
325QSet<int> InputController::activeGamepadButtons(int type) {
326 QSet<int> activeButtons;
327#ifdef BUILD_SDL
328 if (m_playerAttached && type == SDL_BINDING_BUTTON) {
329 SDL_Joystick* joystick = m_sdlPlayer.joystick;
330 SDL_JoystickUpdate();
331 int numButtons = SDL_JoystickNumButtons(joystick);
332 int i;
333 for (i = 0; i < numButtons; ++i) {
334 if (SDL_JoystickGetButton(joystick, i)) {
335 activeButtons.insert(i);
336 }
337 }
338 }
339#endif
340 return activeButtons;
341}
342
343void InputController::recalibrateAxes() {
344#ifdef BUILD_SDL
345 if (m_playerAttached) {
346 SDL_Joystick* joystick = m_sdlPlayer.joystick;
347 SDL_JoystickUpdate();
348 int numAxes = SDL_JoystickNumAxes(joystick);
349 if (numAxes < 1) {
350 return;
351 }
352 m_deadzones.resize(numAxes);
353 int i;
354 for (i = 0; i < numAxes; ++i) {
355 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
356 }
357 }
358#endif
359}
360
361QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
362 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
363#ifdef BUILD_SDL
364 if (m_playerAttached && type == SDL_BINDING_BUTTON) {
365 SDL_Joystick* joystick = m_sdlPlayer.joystick;
366 SDL_JoystickUpdate();
367 int numAxes = SDL_JoystickNumAxes(joystick);
368 if (numAxes < 1) {
369 return activeAxes;
370 }
371 m_deadzones.resize(numAxes);
372 int i;
373 for (i = 0; i < numAxes; ++i) {
374 int32_t axis = SDL_JoystickGetAxis(joystick, i);
375 axis -= m_deadzones[i];
376 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
377 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
378 }
379 }
380 }
381#endif
382 return activeAxes;
383}
384
385void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
386 const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, type, axis);
387 GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
388 if (old) {
389 description = *old;
390 }
391 switch (direction) {
392 case GamepadAxisEvent::NEGATIVE:
393 description.lowDirection = key;
394 description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
395 break;
396 case GamepadAxisEvent::POSITIVE:
397 description.highDirection = key;
398 description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
399 break;
400 default:
401 return;
402 }
403 GBAInputBindAxis(&m_inputMap, type, axis, &description);
404}
405
406void InputController::testGamepad(int type) {
407 auto activeAxes = activeGamepadAxes(type);
408 auto oldAxes = m_activeAxes;
409 m_activeAxes = activeAxes;
410
411 auto activeButtons = activeGamepadButtons(type);
412 auto oldButtons = m_activeButtons;
413 m_activeButtons = activeButtons;
414
415 if (!QApplication::focusWidget()) {
416 return;
417 }
418
419 activeAxes.subtract(oldAxes);
420 oldAxes.subtract(m_activeAxes);
421
422 for (auto& axis : m_activeAxes) {
423 bool newlyAboveThreshold = activeAxes.contains(axis);
424 if (newlyAboveThreshold) {
425 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
426 postPendingEvent(event->gbaKey());
427 QApplication::sendEvent(QApplication::focusWidget(), event);
428 if (!event->isAccepted()) {
429 clearPendingEvent(event->gbaKey());
430 }
431 }
432 }
433 for (auto axis : oldAxes) {
434 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
435 clearPendingEvent(event->gbaKey());
436 QApplication::sendEvent(QApplication::focusWidget(), event);
437 }
438
439 if (!QApplication::focusWidget()) {
440 return;
441 }
442
443 activeButtons.subtract(oldButtons);
444 oldButtons.subtract(m_activeButtons);
445
446 for (int button : activeButtons) {
447 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
448 postPendingEvent(event->gbaKey());
449 QApplication::sendEvent(QApplication::focusWidget(), event);
450 if (!event->isAccepted()) {
451 clearPendingEvent(event->gbaKey());
452 }
453 }
454 for (int button : oldButtons) {
455 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
456 clearPendingEvent(event->gbaKey());
457 QApplication::sendEvent(QApplication::focusWidget(), event);
458 }
459}
460
461void InputController::postPendingEvent(GBAKey key) {
462 m_pendingEvents.insert(key);
463}
464
465void InputController::clearPendingEvent(GBAKey key) {
466 m_pendingEvents.remove(key);
467}
468
469bool InputController::hasPendingEvent(GBAKey key) const {
470 return m_pendingEvents.contains(key);
471}
472
473#if defined(BUILD_SDL) && SDL_VERSION_ATLEAST(2, 0, 0)
474void InputController::suspendScreensaver() {
475 GBASDLSuspendScreensaver(&s_sdlEvents);
476}
477
478void InputController::resumeScreensaver() {
479 GBASDLResumeScreensaver(&s_sdlEvents);
480}
481
482void InputController::setScreensaverSuspendable(bool suspendable) {
483 GBASDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
484}
485#endif