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