1 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
2 
3 #include <QRegExp>
4 
5 #include "joystick.h"
6 #include "arcadesettings.h"
7 #include "tweakedqmlappviewer.h"
8 #include "macros.h"
9 
10 extern ArcadeSettings *globalConfig;
11 
Joystick(QObject * parent,int joystickEventTimeout,bool doAutoRepeat,int repeatDelay)12 Joystick::Joystick(QObject *parent, int joystickEventTimeout, bool doAutoRepeat, int repeatDelay)
13 	: QObject(parent)
14 {
15 	if ( SDL_Init(SDL_INIT_JOYSTICK) == 0 ) {
16 		QRegExp rx("(\\b.*\\b)\\1");
17 		for (int i = 0; i < SDL_NumJoysticks(); i++) {
18 #if SDL_MAJOR_VERSION == 1
19 			QString jsName(SDL_JoystickName(i));
20 #elif SDL_MAJOR_VERSION == 2
21 			SDL_Joystick *js = SDL_JoystickOpen(i);
22 			QString jsName(SDL_JoystickName(js));
23 			SDL_JoystickClose(js);
24 #endif
25 			jsName.replace(rx, "\\1"); // remove consecutive duplicate words in the joystick name (i. e. "Logitech Logitech Extreme 3D" becomes "Logitech Extreme 3D")
26 			joystickNames.append(jsName);
27 		}
28 		connect(&joystickTimer, SIGNAL(timeout()), this, SLOT(processEvents()));
29 	} else
30 		QMC2_ARCADE_LOG_STR(tr("ERROR: couldn't initialize SDL joystick support"));
31 	joystick = 0;
32 	jsIndex = -1;
33 	numAxes = numButtons = numHats = numTrackballs = 0;
34 	autoRepeat = doAutoRepeat;
35 	autoRepeatDelay = repeatDelay;
36 	eventTimeout = joystickEventTimeout;
37 }
38 
~Joystick()39 Joystick::~Joystick()
40 {
41 	if ( isOpen() )
42 		close();
43 	SDL_Quit();
44 }
45 
open(int stick)46 bool Joystick::open(int stick)
47 {
48 	if ( isOpen() )
49 		close();
50 	joystick = SDL_JoystickOpen(stick);
51 	if ( joystick ) {
52 		numAxes = SDL_JoystickNumAxes(joystick);
53 		numButtons = SDL_JoystickNumButtons(joystick);
54 		numHats = SDL_JoystickNumHats(joystick);
55 		numTrackballs = SDL_JoystickNumBalls(joystick);
56 		QMC2_ARCADE_LOG_STR(tr("SDL joystick #%1 opened: name = %2, axes = %3, buttons = %4, hats = %5, trackballs = %6").arg(stick).arg(joystickNames[stick]).arg(numAxes).arg(numButtons).arg(numHats).arg(numTrackballs));
57 		joystickTimer.start(eventTimeout);
58 		jsIndex = stick;
59 		deadzones.clear();
60 		sensitivities.clear();
61 		for (int axis = 0; axis < numAxes; axis++) {
62 			deadzones[axis] = globalConfig->joystickDeadzone(jsIndex, axis);
63 			sensitivities[axis] = globalConfig->joystickSensitivity(jsIndex, axis);
64 		}
65 		return true;
66 	} else {
67 		jsIndex = -1;
68 		return false;
69 	}
70 }
71 
close()72 void Joystick::close()
73 {
74 	joystickTimer.stop();
75 	if ( joystick ) {
76 		SDL_JoystickClose(joystick);
77 		QMC2_ARCADE_LOG_STR(tr("SDL joystick #%1 closed").arg(jsIndex));
78 	}
79 	joystick = 0;
80 	jsIndex = -1;
81 	numAxes = numButtons = numHats = numTrackballs = 0;
82 }
83 
processEvents()84 void Joystick::processEvents()
85 {
86 	if ( !isOpen() )
87 		return;
88 	SDL_JoystickUpdate();
89 	for (int i = 0; i < numAxes; i++) {
90 		Sint16 moved = normalizeAxisValue(SDL_JoystickGetAxis(joystick, i), i);
91 		if ( abs(moved) >= deadzones[i] ) {
92 			if ( (moved != axes[i]) ) {
93 				int deltaMoved = abs(axes[i] - moved);
94 				if ( deltaMoved >= sensitivities[i] )
95 					emit axisValueChanged(i, moved);
96 				axes[i] = moved;
97 				axisRepeatTimers[i].restart();
98 			} else if (autoRepeat && moved != 0) {
99 				if ( axisRepeatTimers[i].elapsed() >= autoRepeatDelay ) {
100 					emit axisValueChanged(i, moved);
101 					axes[i] = moved;
102 				}
103 			} else
104 				axisRepeatTimers[i].restart();
105 		} else
106 			emit axisValueChanged(i, 0);
107 	}
108 	for (int i = 0; i < numButtons; i++) {
109 		Uint8 changed = SDL_JoystickGetButton(joystick, i);
110 		if ( (changed != buttons[i]) ) {
111 			emit buttonValueChanged(i, (bool) changed);
112 			buttons[i] = changed;
113 			buttonRepeatTimers[i].restart();
114 		} else if (autoRepeat && changed != 0) {
115 			if ( buttonRepeatTimers[i].elapsed() >= autoRepeatDelay ) {
116 				emit buttonValueChanged(i, (bool) changed);
117 				buttons[i] = changed;
118 			}
119 		} else
120 			buttonRepeatTimers[i].restart();
121 	}
122 	for (int i = 0; i < numHats; i++) {
123 		Uint8 changed = SDL_JoystickGetHat(joystick, i);
124 		if ( (changed != hats[i]) ) {
125 			emit hatValueChanged(i, changed);
126 			hats[i] = changed;
127 			hatRepeatTimers[i].restart();
128 		} else if (autoRepeat && changed != 0) {
129 			if ( hatRepeatTimers[i].elapsed() >= autoRepeatDelay ) {
130 				emit hatValueChanged(i, changed);
131 				hats[i] = changed;
132 			}
133 		} else
134 			hatRepeatTimers[i].restart();
135 	}
136 	for (int i = 0; i < numTrackballs; i++) {
137 		int dx, dy;
138 		SDL_JoystickGetBall(joystick, i, &dx, &dy);
139 		if ( dx != 0 || dy != 0 )
140 			emit trackballValueChanged(i, dx, dy);
141 	}
142 }
143 
getAxisValue(int axis)144 int Joystick::getAxisValue(int axis)
145 {
146 	if ( isOpen() ) {
147 		SDL_JoystickUpdate();
148 		return normalizeAxisValue(SDL_JoystickGetAxis(joystick, axis), axis);
149 	} else
150 		return 0;
151 }
152 
normalizeAxisValue(Sint16 rawValue,int axis)153 Sint16 Joystick::normalizeAxisValue(Sint16 rawValue, int axis)
154 {
155 	int max = globalConfig->joystickAxisMaximum(jsIndex, axis);
156 	int min = globalConfig->joystickAxisMinimum(jsIndex, axis);
157 	if ( max > min ) {
158 		rawValue -= (max + min) / 2;
159 		return 65535.0 * (double)rawValue / (double)(max - min);
160 	} else
161 		return 0;
162 }
163 
164 #endif
165