Merge pull request #4775 from jmarshallnz/empty_episode_playcount
[vuplus_xbmc] / xbmc / input / SDLJoystick.cpp
1 /*
2  *      Copyright (C) 2007-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "system.h"
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"
29
30 #include <math.h>
31
32 #ifdef HAS_SDL_JOYSTICK
33 #include <SDL/SDL.h>
34
35 using namespace std;
36
37 CJoystick::CJoystick()
38 {
39   Reset(true);
40   m_joystickEnabled = false;
41   m_NumAxes = 0;
42   m_AxisId = 0;
43   m_JoyId = 0;
44   m_ButtonId = 0;
45   m_HatId = 0;
46   m_HatState = SDL_HAT_CENTERED;
47   m_ActiveFlags = JACTIVE_NONE;
48   SetDeadzone(0);
49 }
50
51 void CJoystick::OnSettingChanged(const CSetting *setting)
52 {
53   if (setting == NULL)
54     return;
55
56   const std::string &settingId = setting->GetId();
57   if (settingId == "input.enablejoystick")
58     SetEnabled(((CSettingBool*)setting)->GetValue() && PERIPHERALS::CPeripheralImon::GetCountOfImonsConflictWithDInput() == 0);
59 }
60
61 void CJoystick::Initialize()
62 {
63   if (!IsEnabled())
64     return;
65
66   if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) != 0)
67   {
68     CLog::Log(LOGERROR, "(Re)start joystick subsystem failed : %s",SDL_GetError());
69     return;
70   }
71
72   // clear old joystick names
73   m_JoystickNames.clear();
74
75   // any open ones? if so, close them.
76   if (m_Joysticks.size()>0)
77   {
78     for(size_t idJoy = 0; idJoy < m_Joysticks.size(); idJoy++)
79     {
80       // any joysticks unplugged?
81       if(SDL_JoystickOpened(idJoy))
82         SDL_JoystickClose(m_Joysticks[idJoy]);
83     }
84     m_Joysticks.clear();
85     m_JoyId = -1;
86   }
87
88   // Set deadzone range
89   SetDeadzone(g_advancedSettings.m_controllerDeadzone);
90
91   // any joysticks connected?
92   if (SDL_NumJoysticks()>0)
93   {
94     // load joystick names and open all connected joysticks
95     for (int i = 0 ; i<SDL_NumJoysticks() ; i++)
96     {
97       SDL_Joystick *joy = SDL_JoystickOpen(i);
98
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.
102       //
103       if (std::string(SDL_JoystickName(i)).find("360") != std::string::npos)
104       {
105         CLog::Log(LOGNOTICE, "Ignoring joystick: %s", SDL_JoystickName(i));
106         continue;
107       }
108 #endif
109       if (joy)
110       {
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)
124         {
125           CLog::Log(LOGNOTICE, "Ignoring Joystick %s Axis: %d Buttons: %d: invalid device properties",
126            SDL_JoystickName(i), num_axis, num_buttons);
127         }
128         else
129         {
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);
135         }
136       }
137       else
138       {
139         m_JoystickNames.push_back(string(""));
140       }
141     }
142   }
143   
144   // disable joystick events, since we'll be polling them
145   SDL_JoystickEventState(SDL_DISABLE);
146 }
147
148 void CJoystick::Reset(bool axis /*=false*/)
149 {
150   if (axis)
151   {
152     SetAxisActive(false);
153     for (int i = 0 ; i<MAX_AXES ; i++)
154     {
155       ResetAxis(i);
156     }
157   }
158 }
159
160 void CJoystick::Update()
161 {
162   if (!IsEnabled())
163     return;
164
165   int buttonId    = -1;
166   int axisId      = -1;
167   int hatId       = -1;
168   int numj        = m_Joysticks.size();
169   if (numj <= 0)
170     return;
171
172   // update the state of all opened joysticks
173   SDL_JoystickUpdate();
174
175   // go through all joysticks
176   for (int j = 0; j<numj; j++)
177   {
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;
183     int axisval;
184     uint8_t hatval;
185
186     // get button states first, they take priority over axis
187     for (int b = 0 ; b<numb ; b++)
188     {
189       if (SDL_JoystickGetButton(joy, b))
190       {
191         m_JoyId = SDL_JoystickIndex(joy);
192         buttonId = b+1;
193         j = numj-1;
194         break;
195       }
196     }
197     
198     for (int h = 0; h < numhat; h++)
199     {
200       hatval = SDL_JoystickGetHat(joy, h);
201       if (hatval != SDL_HAT_CENTERED)
202       {
203         m_JoyId = SDL_JoystickIndex(joy);
204         hatId = h + 1;
205         m_HatState = hatval;
206         j = numj-1;
207         break; 
208       }
209     }
210
211     // get axis states
212     m_NumAxes = numax;
213     for (int a = 0 ; a<numax ; a++)
214     {
215       axisval = SDL_JoystickGetAxis(joy, a);
216       axisId = a+1;
217       if (axisId<=0 || axisId>=MAX_AXES)
218       {
219         CLog::Log(LOGERROR, "Axis Id out of range. Maximum supported axis: %d", MAX_AXES);
220       }
221       else
222       {
223         m_Amount[axisId] = axisval;  //[-32768 to 32767]
224       }
225     }
226     m_AxisId = GetAxisWithMaxAmount();
227     if (m_AxisId)
228     {
229       m_JoyId = SDL_JoystickIndex(joy);
230       j = numj-1;
231       break;
232     }
233   }
234
235   if(hatId==-1)
236   {
237     if(m_HatId!=0)
238       CLog::Log(LOGDEBUG, "Joystick %d hat %u Centered", m_JoyId, hatId);
239     m_pressTicksHat = 0;
240     SetHatActive(false);
241     m_HatId = 0;
242   }
243   else
244   {
245     if(hatId!=m_HatId)
246     {
247       CLog::Log(LOGDEBUG, "Joystick %d hat %u Down", m_JoyId, hatId);
248       m_HatId = hatId;
249       m_pressTicksHat = SDL_GetTicks();
250     }
251     SetHatActive();
252   }
253
254   if (buttonId==-1)
255   {
256     if (m_ButtonId!=0)
257     {
258       CLog::Log(LOGDEBUG, "Joystick %d button %d Up", m_JoyId, m_ButtonId);
259     }
260     m_pressTicksButton = 0;
261     SetButtonActive(false);
262     m_ButtonId = 0;
263   }
264   else
265   {
266     if (buttonId!=m_ButtonId)
267     {
268       CLog::Log(LOGDEBUG, "Joystick %d button %d Down", m_JoyId, buttonId);
269       m_ButtonId = buttonId;
270       m_pressTicksButton = SDL_GetTicks();
271     }
272     SetButtonActive();
273   }
274
275 }
276
277 void CJoystick::Update(SDL_Event& joyEvent)
278 {
279   if (!IsEnabled())
280     return;
281
282   int buttonId = -1;
283   int axisId = -1;
284   int joyId = -1;
285   DECLARE_UNUSED(bool,ignore = false)
286   DECLARE_UNUSED(bool,axis = false);
287
288   switch(joyEvent.type)
289   {
290   case SDL_JOYBUTTONDOWN:
291     m_JoyId = joyId = joyEvent.jbutton.which;
292     m_ButtonId = buttonId = joyEvent.jbutton.button + 1;
293     m_pressTicksButton = SDL_GetTicks();
294     SetButtonActive();
295     CLog::Log(LOGDEBUG, "Joystick %d button %d Down", joyId, buttonId);
296     break;
297
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)
303     {
304       CLog::Log(LOGERROR, "Axis Id out of range. Maximum supported axis: %d", MAX_AXES);
305       ignore = true;
306       break;
307     }
308     axis = true;
309     m_JoyId = joyId;
310     if (joyEvent.jaxis.value==0)
311     {
312       ignore = true;
313       m_Amount[axisId] = 0;
314     }
315     else
316     {
317       m_Amount[axisId] = joyEvent.jaxis.value; //[-32768 to 32767]
318     }
319     m_AxisId = GetAxisWithMaxAmount();
320     CLog::Log(LOGDEBUG, "Joystick %d Axis %d Amount %d", joyId, axisId, m_Amount[axisId]);
321     break;
322
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);
330     break;
331
332   case SDL_JOYBALLMOTION:
333     ignore = true;
334     break;
335     
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);
340
341   default:
342     ignore = true;
343     break;
344   }
345 }
346
347 bool CJoystick::GetHat(int &id, int &position,bool consider_repeat) 
348 {
349   if (!IsEnabled() || !IsHatActive())
350   {
351     id = position = 0;
352     return false;
353   }
354   position = m_HatState;
355   id = m_HatId;
356   if (!consider_repeat)
357     return true;
358
359   static uint32_t lastPressTicks = 0;
360   static uint32_t lastTicks = 0;
361   static uint32_t nowTicks = 0;
362
363   if ((m_HatId>=0) && m_pressTicksHat)
364   {
365     // return the id if it's the first press
366     if (lastPressTicks!=m_pressTicksHat)
367     {
368       lastPressTicks = m_pressTicksHat;
369       return true;
370     }
371     nowTicks = SDL_GetTicks();
372     if ((nowTicks-m_pressTicksHat)<500) // 500ms delay before we repeat
373       return false;
374     if ((nowTicks-lastTicks)<100) // 100ms delay before successive repeats
375       return false;
376
377     lastTicks = nowTicks;
378   }
379
380   return true;
381
382
383 bool CJoystick::GetButton(int &id, bool consider_repeat)
384 {
385   if (!IsEnabled() || !IsButtonActive())
386   {
387     id = 0;
388     return false;
389   }
390   if (!consider_repeat)
391   {
392     id = m_ButtonId;
393     return true;
394   }
395
396   static uint32_t lastPressTicks = 0;
397   static uint32_t lastTicks = 0;
398   static uint32_t nowTicks = 0;
399
400   if ((m_ButtonId>=0) && m_pressTicksButton)
401   {
402     // return the id if it's the first press
403     if (lastPressTicks!=m_pressTicksButton)
404     {
405       lastPressTicks = m_pressTicksButton;
406       id = m_ButtonId;
407       return true;
408     }
409     nowTicks = SDL_GetTicks();
410     if ((nowTicks-m_pressTicksButton)<500) // 500ms delay before we repeat
411     {
412       return false;
413     }
414     if ((nowTicks-lastTicks)<100) // 100ms delay before successive repeats
415     {
416       return false;
417     }
418     lastTicks = nowTicks;
419   }
420   id = m_ButtonId;
421   return true;
422 }
423
424 bool CJoystick::GetAxis (int &id)
425
426   if (!IsEnabled() || !IsAxisActive()) 
427   {
428     id = 0;
429     return false; 
430   }
431   id = m_AxisId; 
432   return true; 
433 }
434
435 int CJoystick::GetAxisWithMaxAmount()
436 {
437   static int maxAmount;
438   static int axis;
439   axis = 0;
440   maxAmount = 0;
441   int tempf;
442   for (int i = 1 ; i<=m_NumAxes ; i++)
443   {
444     tempf = abs(m_Amount[i]);
445     if (tempf>m_DeadzoneRange && tempf>maxAmount)
446     {
447       maxAmount = tempf;
448       axis = i;
449     }
450   }
451   SetAxisActive(0 != maxAmount);
452   return axis;
453 }
454
455 float CJoystick::GetAmount(int axis)
456 {
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);
461   return 0;
462 }
463
464 void CJoystick::SetEnabled(bool enabled /*=true*/)
465 {
466   if( enabled && !m_joystickEnabled )
467   {
468     m_joystickEnabled = true;
469     Initialize();
470   }
471   else if( !enabled && m_joystickEnabled )
472   {
473     ReleaseJoysticks();
474     m_joystickEnabled = false;
475   }
476 }
477
478 float CJoystick::SetDeadzone(float val)
479 {
480   if (val<0) val=0;
481   if (val>1) val=1;
482   m_DeadzoneRange = (int)(val*MAX_AXISAMOUNT);
483   return val;
484 }
485
486 bool CJoystick::ReleaseJoysticks()
487 {
488   m_Joysticks.clear();
489   m_JoystickNames.clear();
490   m_HatId = 0;
491   m_ButtonId = 0;
492   m_HatState = SDL_HAT_CENTERED;
493   m_ActiveFlags = JACTIVE_NONE;
494   Reset(true);
495
496   // Restart SDL joystick subsystem
497   SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
498   if (SDL_WasInit(SDL_INIT_JOYSTICK) !=  0)
499   {
500     CLog::Log(LOGERROR, "Stop joystick subsystem failed");
501     return false;
502   }
503   return true;
504 }
505
506 bool CJoystick::Reinitialize()
507 {
508   if( !ReleaseJoysticks() ) return false;
509   Initialize();
510
511   return true;
512 }
513
514 #endif