2 * Copyright (C) 2007-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
22 #include "SDLJoystick.h"
23 #include "ButtonTranslator.h"
24 #include "peripherals/devices/PeripheralImon.h"
25 #include "settings/AdvancedSettings.h"
26 #include "settings/lib/Setting.h"
27 #include "utils/log.h"
28 #include "utils/StringUtils.h"
32 #ifdef HAS_SDL_JOYSTICK
37 CJoystick::CJoystick()
40 m_joystickEnabled = false;
46 m_HatState = SDL_HAT_CENTERED;
47 m_ActiveFlags = JACTIVE_NONE;
51 void CJoystick::OnSettingChanged(const CSetting *setting)
56 const std::string &settingId = setting->GetId();
57 if (settingId == "input.enablejoystick")
58 SetEnabled(((CSettingBool*)setting)->GetValue() && PERIPHERALS::CPeripheralImon::GetCountOfImonsConflictWithDInput() == 0);
61 void CJoystick::Initialize()
66 if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) != 0)
68 CLog::Log(LOGERROR, "(Re)start joystick subsystem failed : %s",SDL_GetError());
72 // clear old joystick names
73 m_JoystickNames.clear();
75 // any open ones? if so, close them.
76 if (m_Joysticks.size()>0)
78 for(size_t idJoy = 0; idJoy < m_Joysticks.size(); idJoy++)
80 // any joysticks unplugged?
81 if(SDL_JoystickOpened(idJoy))
82 SDL_JoystickClose(m_Joysticks[idJoy]);
89 SetDeadzone(g_advancedSettings.m_controllerDeadzone);
91 // any joysticks connected?
92 if (SDL_NumJoysticks()>0)
94 // load joystick names and open all connected joysticks
95 for (int i = 0 ; i<SDL_NumJoysticks() ; i++)
97 SDL_Joystick *joy = SDL_JoystickOpen(i);
99 #if defined(TARGET_DARWIN)
100 // On OS X, the 360 controllers are handled externally, since the SDL code is
101 // really buggy and doesn't handle disconnects.
103 if (std::string(SDL_JoystickName(i)).find("360") != std::string::npos)
105 CLog::Log(LOGNOTICE, "Ignoring joystick: %s", SDL_JoystickName(i));
111 // Some (Microsoft) Keyboards are recognized as Joysticks by modern kernels
112 // Don't enumerate them
113 // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/390959
114 // NOTICE: Enabled Joystick: Microsoft Wired Keyboard 600
115 // Details: Total Axis: 37 Total Hats: 0 Total Buttons: 57
116 // NOTICE: Enabled Joystick: Microsoft Microsoft® 2.4GHz Transceiver v6.0
117 // Details: Total Axis: 37 Total Hats: 0 Total Buttons: 57
118 // also checks if we have at least 1 button, fixes
119 // NOTICE: Enabled Joystick: ST LIS3LV02DL Accelerometer
120 // Details: Total Axis: 3 Total Hats: 0 Total Buttons: 0
121 int num_axis = SDL_JoystickNumAxes(joy);
122 int num_buttons = SDL_JoystickNumButtons(joy);
123 if ((num_axis > 20 && num_buttons > 50) || num_buttons == 0)
125 CLog::Log(LOGNOTICE, "Ignoring Joystick %s Axis: %d Buttons: %d: invalid device properties",
126 SDL_JoystickName(i), num_axis, num_buttons);
130 m_JoystickNames.push_back(string(SDL_JoystickName(i)));
131 CLog::Log(LOGNOTICE, "Enabled Joystick: %s", SDL_JoystickName(i));
132 CLog::Log(LOGNOTICE, "Details: Total Axis: %d Total Hats: %d Total Buttons: %d",
133 num_axis, SDL_JoystickNumHats(joy), num_buttons);
134 m_Joysticks.push_back(joy);
139 m_JoystickNames.push_back(string(""));
144 // disable joystick events, since we'll be polling them
145 SDL_JoystickEventState(SDL_DISABLE);
148 void CJoystick::Reset(bool axis /*=false*/)
152 SetAxisActive(false);
153 for (int i = 0 ; i<MAX_AXES ; i++)
160 void CJoystick::Update()
168 int numj = m_Joysticks.size();
172 // update the state of all opened joysticks
173 SDL_JoystickUpdate();
175 // go through all joysticks
176 for (int j = 0; j<numj; j++)
178 SDL_Joystick *joy = m_Joysticks[j];
179 int numb = SDL_JoystickNumButtons(joy);
180 int numhat = SDL_JoystickNumHats(joy);
181 int numax = SDL_JoystickNumAxes(joy);
182 numax = (numax>MAX_AXES)?MAX_AXES:numax;
186 // get button states first, they take priority over axis
187 for (int b = 0 ; b<numb ; b++)
189 if (SDL_JoystickGetButton(joy, b))
191 m_JoyId = SDL_JoystickIndex(joy);
198 for (int h = 0; h < numhat; h++)
200 hatval = SDL_JoystickGetHat(joy, h);
201 if (hatval != SDL_HAT_CENTERED)
203 m_JoyId = SDL_JoystickIndex(joy);
213 for (int a = 0 ; a<numax ; a++)
215 axisval = SDL_JoystickGetAxis(joy, a);
217 if (axisId<=0 || axisId>=MAX_AXES)
219 CLog::Log(LOGERROR, "Axis Id out of range. Maximum supported axis: %d", MAX_AXES);
223 m_Amount[axisId] = axisval; //[-32768 to 32767]
226 m_AxisId = GetAxisWithMaxAmount();
229 m_JoyId = SDL_JoystickIndex(joy);
238 CLog::Log(LOGDEBUG, "Joystick %d hat %u Centered", m_JoyId, hatId);
247 CLog::Log(LOGDEBUG, "Joystick %d hat %u Down", m_JoyId, hatId);
249 m_pressTicksHat = SDL_GetTicks();
258 CLog::Log(LOGDEBUG, "Joystick %d button %d Up", m_JoyId, m_ButtonId);
260 m_pressTicksButton = 0;
261 SetButtonActive(false);
266 if (buttonId!=m_ButtonId)
268 CLog::Log(LOGDEBUG, "Joystick %d button %d Down", m_JoyId, buttonId);
269 m_ButtonId = buttonId;
270 m_pressTicksButton = SDL_GetTicks();
277 void CJoystick::Update(SDL_Event& joyEvent)
285 DECLARE_UNUSED(bool,ignore = false)
286 DECLARE_UNUSED(bool,axis = false);
288 switch(joyEvent.type)
290 case SDL_JOYBUTTONDOWN:
291 m_JoyId = joyId = joyEvent.jbutton.which;
292 m_ButtonId = buttonId = joyEvent.jbutton.button + 1;
293 m_pressTicksButton = SDL_GetTicks();
295 CLog::Log(LOGDEBUG, "Joystick %d button %d Down", joyId, buttonId);
298 case SDL_JOYAXISMOTION:
299 joyId = joyEvent.jaxis.which;
300 axisId = joyEvent.jaxis.axis + 1;
301 m_NumAxes = SDL_JoystickNumAxes(m_Joysticks[joyId]);
302 if (axisId<=0 || axisId>=MAX_AXES)
304 CLog::Log(LOGERROR, "Axis Id out of range. Maximum supported axis: %d", MAX_AXES);
310 if (joyEvent.jaxis.value==0)
313 m_Amount[axisId] = 0;
317 m_Amount[axisId] = joyEvent.jaxis.value; //[-32768 to 32767]
319 m_AxisId = GetAxisWithMaxAmount();
320 CLog::Log(LOGDEBUG, "Joystick %d Axis %d Amount %d", joyId, axisId, m_Amount[axisId]);
323 case SDL_JOYHATMOTION:
324 m_JoyId = joyId = joyEvent.jbutton.which;
325 m_HatId = joyEvent.jhat.hat + 1;
326 m_pressTicksHat = SDL_GetTicks();
327 m_HatState = joyEvent.jhat.value;
328 SetHatActive(m_HatState != SDL_HAT_CENTERED);
329 CLog::Log(LOGDEBUG, "Joystick %d Hat %d Down with position %d", joyId, buttonId, m_HatState);
332 case SDL_JOYBALLMOTION:
336 case SDL_JOYBUTTONUP:
337 m_pressTicksButton = 0;
338 SetButtonActive(false);
339 CLog::Log(LOGDEBUG, "Joystick %d button %d Up", joyEvent.jbutton.which, m_ButtonId);
347 bool CJoystick::GetHat(int &id, int &position,bool consider_repeat)
349 if (!IsEnabled() || !IsHatActive())
354 position = m_HatState;
356 if (!consider_repeat)
359 static uint32_t lastPressTicks = 0;
360 static uint32_t lastTicks = 0;
361 static uint32_t nowTicks = 0;
363 if ((m_HatId>=0) && m_pressTicksHat)
365 // return the id if it's the first press
366 if (lastPressTicks!=m_pressTicksHat)
368 lastPressTicks = m_pressTicksHat;
371 nowTicks = SDL_GetTicks();
372 if ((nowTicks-m_pressTicksHat)<500) // 500ms delay before we repeat
374 if ((nowTicks-lastTicks)<100) // 100ms delay before successive repeats
377 lastTicks = nowTicks;
383 bool CJoystick::GetButton(int &id, bool consider_repeat)
385 if (!IsEnabled() || !IsButtonActive())
390 if (!consider_repeat)
396 static uint32_t lastPressTicks = 0;
397 static uint32_t lastTicks = 0;
398 static uint32_t nowTicks = 0;
400 if ((m_ButtonId>=0) && m_pressTicksButton)
402 // return the id if it's the first press
403 if (lastPressTicks!=m_pressTicksButton)
405 lastPressTicks = m_pressTicksButton;
409 nowTicks = SDL_GetTicks();
410 if ((nowTicks-m_pressTicksButton)<500) // 500ms delay before we repeat
414 if ((nowTicks-lastTicks)<100) // 100ms delay before successive repeats
418 lastTicks = nowTicks;
424 bool CJoystick::GetAxis (int &id)
426 if (!IsEnabled() || !IsAxisActive())
435 int CJoystick::GetAxisWithMaxAmount()
437 static int maxAmount;
442 for (int i = 1 ; i<=m_NumAxes ; i++)
444 tempf = abs(m_Amount[i]);
445 if (tempf>m_DeadzoneRange && tempf>maxAmount)
451 SetAxisActive(0 != maxAmount);
455 float CJoystick::GetAmount(int axis)
457 if (m_Amount[axis] > m_DeadzoneRange)
458 return (float)(m_Amount[axis]-m_DeadzoneRange)/(float)(MAX_AXISAMOUNT-m_DeadzoneRange);
459 if (m_Amount[axis] < -m_DeadzoneRange)
460 return (float)(m_Amount[axis]+m_DeadzoneRange)/(float)(MAX_AXISAMOUNT-m_DeadzoneRange);
464 void CJoystick::SetEnabled(bool enabled /*=true*/)
466 if( enabled && !m_joystickEnabled )
468 m_joystickEnabled = true;
471 else if( !enabled && m_joystickEnabled )
474 m_joystickEnabled = false;
478 float CJoystick::SetDeadzone(float val)
482 m_DeadzoneRange = (int)(val*MAX_AXISAMOUNT);
486 bool CJoystick::ReleaseJoysticks()
489 m_JoystickNames.clear();
492 m_HatState = SDL_HAT_CENTERED;
493 m_ActiveFlags = JACTIVE_NONE;
496 // Restart SDL joystick subsystem
497 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
498 if (SDL_WasInit(SDL_INIT_JOYSTICK) != 0)
500 CLog::Log(LOGERROR, "Stop joystick subsystem failed");
506 bool CJoystick::Reinitialize()
508 if( !ReleaseJoysticks() ) return false;