1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Gamepad module
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36 #include "qsdlgamepadbackend_p.h"
37 
38 #include <QtCore/QDebug>
39 
40 #include <SDL.h>
41 // Reset bool redefinition from SDL header
42 #undef bool
43 
44 QT_BEGIN_NAMESPACE
45 
QSdlGamepadBackend(QObject * parent)46 QSdlGamepadBackend::QSdlGamepadBackend(QObject *parent)
47     : QGamepadBackend(parent)
48 {
49     connect(&m_eventLoopTimer, SIGNAL(timeout()), this, SLOT(pumpSdlEventLoop()));
50 }
51 
~QSdlGamepadBackend()52 QSdlGamepadBackend::~QSdlGamepadBackend()
53 {
54 }
55 
pumpSdlEventLoop()56 void QSdlGamepadBackend::pumpSdlEventLoop()
57 {
58     SDL_Event event;
59     while (SDL_PollEvent(&event)) {
60         if (event.type == SDL_CONTROLLERAXISMOTION) {
61             SDL_ControllerAxisEvent axisEvent = event.caxis;
62             //qDebug() << axisEvent.timestamp << "Axis Event: " << axisEvent.which << axisEvent.axis << axisEvent.value;
63             double value;
64             if (axisEvent.value >= 0)
65                 value = axisEvent.value / 32767.0;
66             else
67                 value = axisEvent.value / 32768.0;
68             switch (axisEvent.axis) {
69                 case SDL_CONTROLLER_AXIS_LEFTX:
70                     emit gamepadAxisMoved(m_instanceIdForIndex[axisEvent.which], QGamepadManager::AxisLeftX, value);
71                     break;
72                 case SDL_CONTROLLER_AXIS_LEFTY:
73                     emit gamepadAxisMoved(m_instanceIdForIndex[axisEvent.which], QGamepadManager::AxisLeftY, value);
74                     break;
75                 case SDL_CONTROLLER_AXIS_RIGHTX:
76                     emit gamepadAxisMoved(m_instanceIdForIndex[axisEvent.which], QGamepadManager::AxisRightX, value);
77                     break;
78                 case SDL_CONTROLLER_AXIS_RIGHTY:
79                     emit gamepadAxisMoved(m_instanceIdForIndex[axisEvent.which], QGamepadManager::AxisRightY, value);
80                     break;
81                 case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
82                     if (value == 0)
83                         emit gamepadButtonReleased(m_instanceIdForIndex[axisEvent.which], QGamepadManager::ButtonL2);
84                     else
85                         emit gamepadButtonPressed(m_instanceIdForIndex[axisEvent.which], QGamepadManager::ButtonL2, value);
86                     break;
87                 case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
88                     if (value == 0)
89                         emit gamepadButtonReleased(m_instanceIdForIndex[axisEvent.which], QGamepadManager::ButtonR2);
90                     else
91                         emit gamepadButtonPressed(m_instanceIdForIndex[axisEvent.which], QGamepadManager::ButtonR2, value);
92                     break;
93                 default:
94                     break;
95             }
96 
97         } else if (event.type == SDL_CONTROLLERBUTTONDOWN) {
98             SDL_ControllerButtonEvent buttonEvent = event.cbutton;
99             //qDebug() << buttonEvent.timestamp << "Button Press: " << buttonEvent.which << buttonEvent.button << buttonEvent.state;
100             emit gamepadButtonPressed(m_instanceIdForIndex[buttonEvent.which], translateButton(buttonEvent.button), 1.0);
101         } else if (event.type == SDL_CONTROLLERBUTTONUP) {
102             SDL_ControllerButtonEvent buttonEvent = event.cbutton;
103             //qDebug() << buttonEvent.timestamp << "Button Release: " << buttonEvent.which << buttonEvent.button << buttonEvent.state;
104             emit gamepadButtonReleased(m_instanceIdForIndex[buttonEvent.which], translateButton(buttonEvent.button));
105         } else if (event.type == SDL_CONTROLLERDEVICEADDED) {
106             SDL_ControllerDeviceEvent deviceEvent = event.cdevice;
107             //qDebug() << deviceEvent.timestamp << "Controller Added: " << deviceEvent.which;
108             addController(deviceEvent.which);
109         } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
110             SDL_ControllerDeviceEvent deviceEvent = event.cdevice;
111 
112             int index = m_instanceIdForIndex[deviceEvent.which];
113             SDL_GameControllerClose(m_indexForController[index]);
114             emit gamepadRemoved(index);
115             m_indexForController.remove(index);
116             m_instanceIdForIndex.remove(deviceEvent.which);
117 
118         } else if (event.type == SDL_CONTROLLERDEVICEREMAPPED) {
119             //SDL_ControllerDeviceEvent deviceEvent = event.cdevice;
120             //qDebug() << deviceEvent.timestamp << "Controller Remapped: " << deviceEvent.which;
121         }
122     }
123 }
124 
start()125 bool QSdlGamepadBackend::start()
126 {
127     //Initialize SDL with necessary subsystems for gamepad support
128     if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK)) {
129         qDebug() << SDL_GetError();
130         return false;
131     }
132 
133     m_eventLoopTimer.start(16);
134     for (int i = 0; i < SDL_NumJoysticks() ; i++)
135         addController(i);
136 
137     return true;
138 }
139 
stop()140 void QSdlGamepadBackend::stop()
141 {
142     m_eventLoopTimer.stop();
143     SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK);
144 }
145 
translateButton(int button)146 QGamepadManager::GamepadButton QSdlGamepadBackend::translateButton(int button)
147 {
148     switch (button) {
149     case SDL_CONTROLLER_BUTTON_A:
150         return QGamepadManager::ButtonA;
151     case SDL_CONTROLLER_BUTTON_B:
152         return QGamepadManager::ButtonB;
153     case SDL_CONTROLLER_BUTTON_X:
154         return QGamepadManager::ButtonX;
155     case SDL_CONTROLLER_BUTTON_Y:
156         return QGamepadManager::ButtonY;
157     case SDL_CONTROLLER_BUTTON_BACK:
158         return QGamepadManager::ButtonSelect;
159     case SDL_CONTROLLER_BUTTON_GUIDE:
160         return QGamepadManager::ButtonGuide;
161     case SDL_CONTROLLER_BUTTON_START:
162         return QGamepadManager::ButtonStart;
163     case SDL_CONTROLLER_BUTTON_LEFTSTICK:
164         return QGamepadManager::ButtonL3;
165     case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
166         return QGamepadManager::ButtonR3;
167     case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
168         return QGamepadManager::ButtonL1;
169     case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
170         return QGamepadManager::ButtonR1;
171     case SDL_CONTROLLER_BUTTON_DPAD_UP:
172         return QGamepadManager::ButtonUp;
173     case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
174         return QGamepadManager::ButtonDown;
175     case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
176         return QGamepadManager::ButtonLeft;
177     case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
178         return QGamepadManager::ButtonRight;
179     default:
180         return QGamepadManager::ButtonInvalid;
181     }
182 }
183 
addController(int index)184 void QSdlGamepadBackend::addController(int index)
185 {
186     char GUID[100];
187     SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(index), GUID, 100);
188     if (!SDL_IsGameController(index))
189         return;
190 
191     SDL_GameController *controller = SDL_GameControllerOpen(index);
192     if (controller) {
193         m_indexForController.insert(index, controller);
194 
195         SDL_Joystick *joystick = SDL_GameControllerGetJoystick(controller);
196 
197         int instanceID = SDL_JoystickInstanceID(joystick);
198         m_instanceIdForIndex.insert(instanceID, index);
199 
200         const char *name = SDL_JoystickName(joystick);
201 
202         //qDebug() << "Controller " << index << " added with instanceId: " << instanceID;
203         emit gamepadAdded(index);
204 
205         if (name)
206             emit gamepadNamed(index, QString::fromUtf8(name));
207     }
208 }
209 
210 QT_END_NAMESPACE
211