1 /* -------------------------------------------------------
2 Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es)
3 
4 Permission is hereby granted, free of charge, to any
5 person obtaining a copy of this software and associated
6 documentation files (the "Software"), to deal in the
7 Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish,
9 distribute, sublicense, and/or sell copies of the
10 Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice
14 shall be included in all copies or substantial portions of
15 the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
18 KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
19 WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
20 PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
21 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 ------------------------------------------------------- */
26 
27 #include "ICSInputControlSystem.h"
28 
29 #define SDL_JOY_AXIS_MIN -32768
30 #define SDL_JOY_AXIS_MAX 32767
31 
32 namespace ICS
33 {
34 	// load xml
loadJoystickAxisBinders(TiXmlElement * xmlControlNode)35 	void InputControlSystem::loadJoystickAxisBinders(TiXmlElement* xmlControlNode)
36 	{
37 		TiXmlElement* xmlJoystickBinder = xmlControlNode->FirstChildElement("JoystickAxisBinder");
38 		while(xmlJoystickBinder)
39 		{
40 			Control::ControlChangingDirection dir = Control::STOP;
41 			if(std::string(xmlJoystickBinder->Attribute("direction")) == "INCREASE")
42 			{
43 				dir = Control::INCREASE;
44 			}
45 			else if(std::string(xmlJoystickBinder->Attribute("direction")) == "DECREASE")
46 			{
47 				dir = Control::DECREASE;
48 			}
49 
50 			addJoystickAxisBinding(mControls.back(), FromString<int>(xmlJoystickBinder->Attribute("deviceId")), FromString<int>(xmlJoystickBinder->Attribute("axis")), dir);
51 
52 			xmlJoystickBinder = xmlJoystickBinder->NextSiblingElement("JoystickAxisBinder");
53 		}
54 	}
55 
loadJoystickButtonBinders(TiXmlElement * xmlControlNode)56 	void InputControlSystem::loadJoystickButtonBinders(TiXmlElement* xmlControlNode)
57 	{
58 		TiXmlElement* xmlJoystickButtonBinder = xmlControlNode->FirstChildElement("JoystickButtonBinder");
59 		while(xmlJoystickButtonBinder)
60 		{
61 			Control::ControlChangingDirection dir = Control::STOP;
62 			if(std::string(xmlJoystickButtonBinder->Attribute("direction")) == "INCREASE")
63 			{
64 				dir = Control::INCREASE;
65 			}
66 			else if(std::string(xmlJoystickButtonBinder->Attribute("direction")) == "DECREASE")
67 			{
68 				dir = Control::DECREASE;
69 			}
70 
71 			addJoystickButtonBinding(mControls.back(), FromString<int>(xmlJoystickButtonBinder->Attribute("deviceId")), FromString<int>(xmlJoystickButtonBinder->Attribute("button")), dir);
72 
73 			xmlJoystickButtonBinder = xmlJoystickButtonBinder->NextSiblingElement("JoystickButtonBinder");
74 		}
75 	}
76 
77 	// add bindings
addJoystickAxisBinding(Control * control,int deviceID,int axis,Control::ControlChangingDirection direction)78 	void InputControlSystem::addJoystickAxisBinding(Control* control, int deviceID, int axis, Control::ControlChangingDirection direction)
79 	{
80 		if (std::find(mJoystickIDList.begin(), mJoystickIDList.end(), deviceID) == mJoystickIDList.end())
81 			mJoystickIDList.push_back(deviceID);
82 
83 		ICS_LOG("\tAdding AxisBinder [axis="
84 			+ ToString<int>(axis)       + ", deviceID="
85 			+ ToString<int>(deviceID)   + ", direction="
86 			+ ToString<int>(direction)  + "]");
87 
88         control->setValue(0.5f); //all joystick axis start at .5, so do that
89 
90 		ControlAxisBinderItem controlAxisBinderItem;
91 		controlAxisBinderItem.control = control;
92 		controlAxisBinderItem.direction = direction;
93 		mControlsJoystickAxisBinderMap[deviceID][axis] = controlAxisBinderItem;
94 	}
95 
addJoystickButtonBinding(Control * control,int deviceID,unsigned int button,Control::ControlChangingDirection direction)96 	void InputControlSystem::addJoystickButtonBinding(Control* control, int deviceID, unsigned int button, Control::ControlChangingDirection direction)
97 	{
98 		if (std::find(mJoystickIDList.begin(), mJoystickIDList.end(), deviceID) == mJoystickIDList.end())
99 			mJoystickIDList.push_back(deviceID); // Hack: add the device to the list so bindings are saved in save() even when joystick is not connected
100 
101 		ICS_LOG("\tAdding JoystickButtonBinder [button="
102 			+ ToString<int>(button)     + ", deviceID="
103 			+ ToString<int>(deviceID)   + ", direction="
104 			+ ToString<int>(direction)  + "]");
105 
106 		ControlButtonBinderItem controlJoystickButtonBinderItem;
107 		controlJoystickButtonBinderItem.direction = direction;
108 		controlJoystickButtonBinderItem.control = control;
109 		mControlsJoystickButtonBinderMap[deviceID][button] = controlJoystickButtonBinderItem;
110 	}
111 
isJoystickButtonBound(int deviceID,unsigned int button) const112 	bool InputControlSystem::isJoystickButtonBound(int deviceID, unsigned int button) const
113 	{
114 		JoystickButtonBinderMapType::const_iterator found = mControlsJoystickButtonBinderMap.find(deviceID);
115 		if (found == mControlsJoystickButtonBinderMap.end())
116 			return false;
117 
118 		return (found->second.find(button) != found->second.end());
119 	}
120 
isJoystickAxisBound(int deviceID,unsigned int axis) const121 	bool InputControlSystem::isJoystickAxisBound(int deviceID, unsigned int axis) const
122 	{
123 		JoystickAxisBinderMapType::const_iterator found = mControlsJoystickAxisBinderMap.find(deviceID);
124 		if (found == mControlsJoystickAxisBinderMap.end())
125 			return false;
126 
127 		return (found->second.find(axis) != found->second.end());
128 	}
129 
130 	// get bindings
getJoystickAxisBinding(Control * control,int deviceID,ICS::Control::ControlChangingDirection direction)131 	int InputControlSystem::getJoystickAxisBinding(Control* control, int deviceID, ICS::Control::ControlChangingDirection direction)
132 	{
133         if(mControlsJoystickAxisBinderMap.find(deviceID) != mControlsJoystickAxisBinderMap.end())
134 		{
135             ControlsAxisBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceID].begin();
136             while(it != mControlsJoystickAxisBinderMap[deviceID].end())
137             {
138                 if(it->first >= 0 && it->second.control == control && it->second.direction == direction)
139                 {
140                     return it->first;
141                 }
142                 ++it;
143             }
144         }
145 
146 		return /*NamedAxis::*/UNASSIGNED;
147 	}
148 
getJoystickButtonBinding(Control * control,int deviceID,ICS::Control::ControlChangingDirection direction)149 	unsigned int InputControlSystem::getJoystickButtonBinding(Control* control, int deviceID, ICS::Control::ControlChangingDirection direction)
150 	{
151         if(mControlsJoystickButtonBinderMap.find(deviceID) != mControlsJoystickButtonBinderMap.end())
152 		{
153             ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceID].begin();
154             while(it != mControlsJoystickButtonBinderMap[deviceID].end())
155             {
156                 if(it->second.control == control && it->second.direction == direction)
157                 {
158                     return it->first;
159                 }
160                 ++it;
161             }
162         }
163 
164 		return ICS_MAX_DEVICE_BUTTONS;
165 	}
166 
167 	// remove bindings
removeJoystickAxisBinding(int deviceID,int axis)168 	void InputControlSystem::removeJoystickAxisBinding(int deviceID, int axis)
169 	{
170         if(mControlsJoystickAxisBinderMap.find(deviceID) != mControlsJoystickAxisBinderMap.end())
171 		{
172             ControlsAxisBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceID].find(axis);
173             if(it != mControlsJoystickAxisBinderMap[deviceID].end())
174             {
175                 mControlsJoystickAxisBinderMap[deviceID].erase(it);
176             }
177         }
178 	}
179 
removeJoystickButtonBinding(int deviceID,unsigned int button)180 	void InputControlSystem::removeJoystickButtonBinding(int deviceID, unsigned int button)
181 	{
182         if(mControlsJoystickButtonBinderMap.find(deviceID) != mControlsJoystickButtonBinderMap.end())
183 		{
184             ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceID].find(button);
185             if(it != mControlsJoystickButtonBinderMap[deviceID].end())
186             {
187                 mControlsJoystickButtonBinderMap[deviceID].erase(it);
188             }
189         }
190 	}
191 
192 	// joyStick listeners
buttonPressed(int deviceID,const SDL_ControllerButtonEvent & evt)193     void InputControlSystem::buttonPressed(int deviceID, const SDL_ControllerButtonEvent &evt)
194 	{
195 		if(mActive)
196 		{
197 			if(!mDetectingBindingControl)
198 			{
199                 if(mControlsJoystickButtonBinderMap.find(deviceID) != mControlsJoystickButtonBinderMap.end())
200                 {
201                     ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[deviceID].find(evt.button);
202                     if(it != mControlsJoystickButtonBinderMap[deviceID].end())
203                     {
204                         it->second.control->setIgnoreAutoReverse(false);
205                         if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop())
206                         {
207                             it->second.control->setChangingDirection(it->second.direction);
208                         }
209                         else
210                         {
211                             if(it->second.control->getValue() == 1)
212                             {
213                                 it->second.control->setChangingDirection(Control::DECREASE);
214                             }
215                             else if(it->second.control->getValue() == 0)
216                             {
217                                 it->second.control->setChangingDirection(Control::INCREASE);
218                             }
219                         }
220                     }
221                 }
222 			}
223 		}
224 	}
225 
buttonReleased(int deviceID,const SDL_ControllerButtonEvent & evt)226     void InputControlSystem::buttonReleased(int deviceID, const SDL_ControllerButtonEvent &evt)
227 	{
228 		if(mActive)
229 		{
230             if(!mDetectingBindingControl)
231             {
232                 if(mControlsJoystickButtonBinderMap.find(deviceID) != mControlsJoystickButtonBinderMap.end())
233                 {
234                     ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[deviceID].find(evt.button);
235                     if(it != mControlsJoystickButtonBinderMap[deviceID].end())
236                     {
237                         it->second.control->setChangingDirection(Control::STOP);
238                     }
239                 }
240             }
241             else if(mDetectingBindingListener)
242 			{
243 				mDetectingBindingListener->joystickButtonBindingDetected(this, deviceID,
244 					mDetectingBindingControl, evt.button, mDetectingBindingDirection);
245 			}
246 		}
247 	}
248 
axisMoved(int deviceID,const SDL_ControllerAxisEvent & evt)249     void InputControlSystem::axisMoved(int deviceID, const SDL_ControllerAxisEvent &evt)
250 	{
251 		if(mActive)
252 		{
253 			if(!mDetectingBindingControl)
254 			{
255                 if(mControlsJoystickAxisBinderMap.find(deviceID) != mControlsJoystickAxisBinderMap.end())
256                 {
257                     ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[deviceID][evt.axis]; // joystic axis start at 0 index
258                     Control* ctrl = joystickBinderItem.control;
259                     if(ctrl)
260                     {
261                         ctrl->setIgnoreAutoReverse(true);
262 
263                         float axisRange = SDL_JOY_AXIS_MAX - SDL_JOY_AXIS_MIN;
264                         float valDisplaced = (float)(evt.value - SDL_JOY_AXIS_MIN);
265                         float percent = valDisplaced / axisRange * (1+mDeadZone*2) - mDeadZone; //Assures all values, 0 through 1, are seen
266                         if(percent > .5-mDeadZone && percent < .5+mDeadZone) //close enough to center
267                             percent = .5;
268                         else if(percent > .5)
269                             percent -= mDeadZone;
270                         else
271                             percent += mDeadZone;
272 
273                         if(joystickBinderItem.direction == Control::INCREASE)
274                         {
275                             ctrl->setValue( percent );
276                         }
277                         else if(joystickBinderItem.direction == Control::DECREASE)
278                         {
279                             ctrl->setValue( 1 - ( percent ) );
280                         }
281                     }
282                 }
283 			}
284 			else if(mDetectingBindingListener)
285 			{
286 				//ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.which ][ axis ]; // joystic axis start at 0 index
287 				//Control* ctrl = joystickBinderItem.control;
288 				//if(ctrl && ctrl->isAxisBindable())
289 				if(mDetectingBindingControl->isAxisBindable())
290 				{
291 					if( abs( evt.value ) > ICS_JOYSTICK_AXIS_BINDING_MARGIN)
292 					{
293 						mDetectingBindingListener->joystickAxisBindingDetected(this, deviceID,
294 							mDetectingBindingControl, evt.axis, mDetectingBindingDirection);
295 					}
296 				}
297 			}
298 		}
299 	}
300 
controllerAdded(int deviceID,const SDL_ControllerDeviceEvent & args)301     void InputControlSystem::controllerAdded(int deviceID, const SDL_ControllerDeviceEvent &args)
302 	{
303 		ICS_LOG("Adding joystick (index: " + ToString<int>(args.which) + ")");
304 		SDL_GameController* cntrl = SDL_GameControllerOpen(args.which);
305         int instanceID = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(cntrl));
306         if(std::find(mJoystickIDList.begin(), mJoystickIDList.end(), deviceID)==mJoystickIDList.end())
307         {
308             for(int j = 0 ; j < ICS_MAX_JOYSTICK_AXIS ; j++)
309             {
310                 if(mControlsJoystickAxisBinderMap[deviceID].find(j) == mControlsJoystickAxisBinderMap[deviceID].end())
311                 {
312                     ControlAxisBinderItem controlJoystickBinderItem;
313                     controlJoystickBinderItem.direction = Control::STOP;
314                     controlJoystickBinderItem.control = NULL;
315                     mControlsJoystickAxisBinderMap[deviceID][j] = controlJoystickBinderItem;
316                 }
317             }
318             mJoystickIDList.push_front(deviceID);
319 		}
320 
321 		mJoystickInstanceMap[instanceID] = cntrl;
322 	}
controllerRemoved(const SDL_ControllerDeviceEvent & args)323 	void InputControlSystem::controllerRemoved(const SDL_ControllerDeviceEvent &args)
324 	{
325         ICS_LOG("Removing joystick (instance id: " + ToString<int>(args.which) + ")");
326         if(mJoystickInstanceMap.count(args.which)!=0)
327         {
328             SDL_GameControllerClose(mJoystickInstanceMap.at(args.which));
329             mJoystickInstanceMap.erase(args.which);
330         }
331 	}
332 
333 	// joystick auto bindings
joystickAxisBindingDetected(InputControlSystem * ICS,int deviceID,Control * control,int axis,Control::ControlChangingDirection direction)334 	void DetectingBindingListener::joystickAxisBindingDetected(InputControlSystem* ICS, int deviceID, Control* control, int axis, Control::ControlChangingDirection direction)
335 	{
336 		// if the joystick axis is used by another control, remove it
337 		ICS->removeJoystickAxisBinding(deviceID, axis);
338 
339 		// if the control has an axis assigned, remove it
340 		int oldAxis = ICS->getJoystickAxisBinding(control, deviceID, direction);
341 		if(oldAxis != InputControlSystem::UNASSIGNED)
342 		{
343 			ICS->removeJoystickAxisBinding(deviceID, oldAxis);
344 		}
345 
346 		ICS->addJoystickAxisBinding(control, deviceID, axis, direction);
347 		ICS->cancelDetectingBindingState();
348 	}
joystickButtonBindingDetected(InputControlSystem * ICS,int deviceID,Control * control,unsigned int button,Control::ControlChangingDirection direction)349 	void DetectingBindingListener::joystickButtonBindingDetected(InputControlSystem* ICS, int deviceID, Control* control
350 		, unsigned int button, Control::ControlChangingDirection direction)
351 	{
352 		// if the joystick button is used by another control, remove it
353 		ICS->removeJoystickButtonBinding(deviceID, button);
354 
355 		// if the control has a joystick button assigned, remove it
356 		unsigned int oldButton = ICS->getJoystickButtonBinding(control, deviceID, direction);
357 		if(oldButton != ICS_MAX_DEVICE_BUTTONS)
358 		{
359 			ICS->removeJoystickButtonBinding(deviceID, oldButton);
360 		}
361 
362 		ICS->addJoystickButtonBinding(control, deviceID, button, direction);
363 		ICS->cancelDetectingBindingState();
364 	}
365 }
366