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