[bluray] Fix stream info/language retrieval for blurays in non-nav mode.
[vuplus_xbmc] / xbmc / peripherals / devices / PeripheralCecAdapter.cpp
1 /*
2  *      Copyright (C) 2005-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 #if defined(HAVE_LIBCEC)
23 #include "PeripheralCecAdapter.h"
24 #include "input/XBIRRemote.h"
25 #include "Application.h"
26 #include "ApplicationMessenger.h"
27 #include "DynamicDll.h"
28 #include "threads/SingleLock.h"
29 #include "dialogs/GUIDialogKaiToast.h"
30 #include "guilib/GUIWindowManager.h"
31 #include "guilib/Key.h"
32 #include "guilib/LocalizeStrings.h"
33 #include "peripherals/Peripherals.h"
34 #include "peripherals/bus/PeripheralBus.h"
35 #include "pictures/GUIWindowSlideShow.h"
36 #include "settings/Settings.h"
37 #include "utils/log.h"
38 #include "utils/Variant.h"
39
40 #include <libcec/cec.h>
41
42 using namespace PERIPHERALS;
43 using namespace ANNOUNCEMENT;
44 using namespace CEC;
45 using namespace std;
46
47 #define CEC_LIB_SUPPORTED_VERSION 0x2100
48
49 /* time in seconds to ignore standby commands from devices after the screensaver has been activated */
50 #define SCREENSAVER_TIMEOUT       10
51 #define VOLUME_CHANGE_TIMEOUT     250
52 #define VOLUME_REFRESH_TIMEOUT    100
53
54 #define LOCALISED_ID_TV           36037
55 #define LOCALISED_ID_AVR          36038
56 #define LOCALISED_ID_TV_AVR       36039
57 #define LOCALISED_ID_NONE         231
58
59 /* time in seconds to suppress source activation after receiving OnStop */
60 #define CEC_SUPPRESS_ACTIVATE_SOURCE_AFTER_ON_STOP 2
61
62 class DllLibCECInterface
63 {
64 public:
65   virtual ~DllLibCECInterface() {}
66   virtual ICECAdapter* CECInitialise(libcec_configuration *configuration)=0;
67   virtual void*        CECDestroy(ICECAdapter *adapter)=0;
68 };
69
70 class DllLibCEC : public DllDynamic, DllLibCECInterface
71 {
72   DECLARE_DLL_WRAPPER(DllLibCEC, DLL_PATH_LIBCEC)
73
74   DEFINE_METHOD1(ICECAdapter*, CECInitialise, (libcec_configuration *p1))
75   DEFINE_METHOD1(void*       , CECDestroy,    (ICECAdapter *p1))
76
77   BEGIN_METHOD_RESOLVE()
78     RESOLVE_METHOD_RENAME(CECInitialise,  CECInitialise)
79     RESOLVE_METHOD_RENAME(CECDestroy, CECDestroy)
80   END_METHOD_RESOLVE()
81 };
82
83 CPeripheralCecAdapter::CPeripheralCecAdapter(const PeripheralScanResult& scanResult) :
84   CPeripheralHID(scanResult),
85   CThread("CECAdapter"),
86   m_dll(NULL),
87   m_cecAdapter(NULL)
88 {
89   ResetMembers();
90   m_features.push_back(FEATURE_CEC);
91   m_strComPort = scanResult.m_strLocation;
92 }
93
94 CPeripheralCecAdapter::~CPeripheralCecAdapter(void)
95 {
96   {
97     CSingleLock lock(m_critSection);
98     CAnnouncementManager::RemoveAnnouncer(this);
99     m_bStop = true;
100   }
101
102   StopThread(true);
103   delete m_queryThread;
104
105   if (m_dll && m_cecAdapter)
106   {
107     m_dll->CECDestroy(m_cecAdapter);
108     m_cecAdapter = NULL;
109     delete m_dll;
110     m_dll = NULL;
111   }
112 }
113
114 void CPeripheralCecAdapter::ResetMembers(void)
115 {
116   if (m_cecAdapter && m_dll)
117     m_dll->CECDestroy(m_cecAdapter);
118   m_cecAdapter               = NULL;
119   delete m_dll;
120   m_dll                      = NULL;
121   m_bStarted                 = false;
122   m_bHasButton               = false;
123   m_bIsReady                 = false;
124   m_bHasConnectedAudioSystem = false;
125   m_strMenuLanguage          = "???";
126   m_lastKeypress             = 0;
127   m_lastChange               = VOLUME_CHANGE_NONE;
128   m_iExitCode                = 0;
129   m_bIsMuted                 = false; // TODO fetch the correct initial value when system audiostatus is implemented in libCEC
130   m_bGoingToStandby          = false;
131   m_bIsRunning               = false;
132   m_bDeviceRemoved           = false;
133   m_bActiveSourcePending     = false;
134   m_bStandbyPending          = false;
135   m_bActiveSourceBeforeStandby = false;
136   m_bOnPlayReceived          = false;
137   m_bPlaybackPaused          = false;
138   m_queryThread              = NULL;
139
140   m_currentButton.iButton    = 0;
141   m_currentButton.iDuration  = 0;
142   m_screensaverLastActivated.SetValid(false);
143   m_configuration.Clear();
144 }
145
146 void CPeripheralCecAdapter::Announce(AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
147 {
148   if (flag == System && !strcmp(sender, "xbmc") && !strcmp(message, "OnQuit") && m_bIsReady)
149   {
150     CSingleLock lock(m_critSection);
151     m_iExitCode = (int)data.asInteger(0);
152     CAnnouncementManager::RemoveAnnouncer(this);
153     StopThread(false);
154   }
155   else if (flag == GUI && !strcmp(sender, "xbmc") && !strcmp(message, "OnScreensaverDeactivated") && m_bIsReady)
156   {
157     bool bIgnoreDeactivate(false);
158     if (data.isBoolean())
159     {
160       // don't respond to the deactivation if we are just going to suspend/shutdown anyway
161       // the tv will not have time to switch on before being told to standby and
162       // may not action the standby command.
163       bIgnoreDeactivate = data.asBoolean();
164       if (bIgnoreDeactivate)
165         CLog::Log(LOGDEBUG, "%s - ignoring OnScreensaverDeactivated for power action", __FUNCTION__);
166     }
167     if (m_configuration.bPowerOnScreensaver == 1 && !bIgnoreDeactivate &&
168         m_configuration.bActivateSource == 1)
169     {
170       ActivateSource();
171     }
172   }
173   else if (flag == GUI && !strcmp(sender, "xbmc") && !strcmp(message, "OnScreensaverActivated") && m_bIsReady)
174   {
175     // Don't put devices to standby if application is currently playing
176     if ((!g_application.IsPlaying() && !g_application.IsPaused()) && m_configuration.bPowerOffScreensaver == 1)
177     {
178       m_screensaverLastActivated = CDateTime::GetCurrentDateTime();
179       // only power off when we're the active source
180       if (m_cecAdapter->IsLibCECActiveSource())
181         StandbyDevices();
182     }
183   }
184   else if (flag == System && !strcmp(sender, "xbmc") && !strcmp(message, "OnSleep"))
185   {
186     // this will also power off devices when we're the active source
187     {
188       CSingleLock lock(m_critSection);
189       m_bGoingToStandby = true;
190     }
191     StopThread();
192   }
193   else if (flag == System && !strcmp(sender, "xbmc") && !strcmp(message, "OnWake"))
194   {
195     CLog::Log(LOGDEBUG, "%s - reconnecting to the CEC adapter after standby mode", __FUNCTION__);
196     if (ReopenConnection())
197     {
198       bool bActivate(false);
199       {
200         CSingleLock lock(m_critSection);
201         bActivate = m_bActiveSourceBeforeStandby;
202         m_bActiveSourceBeforeStandby = false;
203       }
204       if (bActivate)
205         ActivateSource();
206     }
207   }
208   else if (flag == Player && !strcmp(sender, "xbmc") && !strcmp(message, "OnStop"))
209   {
210     CSingleLock lock(m_critSection);
211     m_preventActivateSourceOnPlay = CDateTime::GetCurrentDateTime();
212     m_bOnPlayReceived = false;
213   }
214   else if (flag == Player && !strcmp(sender, "xbmc") && !strcmp(message, "OnPlay"))
215   {
216     // activate the source when playback started, and the option is enabled
217     bool bActivateSource(false);
218     {
219       CSingleLock lock(m_critSection);
220       bActivateSource = (m_configuration.bActivateSource &&
221           !m_bOnPlayReceived &&
222           !m_cecAdapter->IsLibCECActiveSource() &&
223           (!m_preventActivateSourceOnPlay.IsValid() || CDateTime::GetCurrentDateTime() - m_preventActivateSourceOnPlay > CDateTimeSpan(0, 0, 0, CEC_SUPPRESS_ACTIVATE_SOURCE_AFTER_ON_STOP)));
224       m_bOnPlayReceived = true;
225     }
226     if (bActivateSource)
227       ActivateSource();
228   }
229 }
230
231 bool CPeripheralCecAdapter::InitialiseFeature(const PeripheralFeature feature)
232 {
233   if (feature == FEATURE_CEC && !m_bStarted && GetSettingBool("enabled"))
234   {
235     // hide settings that have an override set
236     if (!GetSettingString("wake_devices_advanced").IsEmpty())
237       SetSettingVisible("wake_devices", false);
238     if (!GetSettingString("standby_devices_advanced").IsEmpty())
239       SetSettingVisible("standby_devices", false);
240
241     SetConfigurationFromSettings();
242     m_callbacks.Clear();
243     m_callbacks.CBCecLogMessage           = &CecLogMessage;
244     m_callbacks.CBCecKeyPress             = &CecKeyPress;
245     m_callbacks.CBCecCommand              = &CecCommand;
246     m_callbacks.CBCecConfigurationChanged = &CecConfiguration;
247     m_callbacks.CBCecAlert                = &CecAlert;
248     m_callbacks.CBCecSourceActivated      = &CecSourceActivated;
249     m_configuration.callbackParam         = this;
250     m_configuration.callbacks             = &m_callbacks;
251
252     m_dll = new DllLibCEC;
253     if (m_dll->Load() && m_dll->IsLoaded())
254       m_cecAdapter = m_dll->CECInitialise(&m_configuration);
255     else
256     {
257       // display warning: libCEC could not be loaded
258       CLog::Log(LOGERROR, "%s", g_localizeStrings.Get(36017).c_str());
259       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(36000), g_localizeStrings.Get(36017));
260       delete m_dll;
261       m_dll = NULL;
262       m_features.clear();
263       return false;
264     }
265
266     if (m_configuration.serverVersion < CEC_LIB_SUPPORTED_VERSION)
267     {
268       /* unsupported libcec version */
269       CLog::Log(LOGERROR, g_localizeStrings.Get(36040).c_str(), m_cecAdapter ? m_configuration.serverVersion : -1, CEC_LIB_SUPPORTED_VERSION);
270
271       // display warning: incompatible libCEC
272       CStdString strMessage;
273       strMessage.Format(g_localizeStrings.Get(36040).c_str(), m_cecAdapter ? m_configuration.serverVersion : -1, CEC_LIB_SUPPORTED_VERSION);
274       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(36000), strMessage);
275       m_bError = true;
276       if (m_cecAdapter)
277         m_dll->CECDestroy(m_cecAdapter);
278       m_cecAdapter = NULL;
279
280       m_features.clear();
281       return false;
282     }
283     else
284     {
285       CLog::Log(LOGDEBUG, "%s - using libCEC v%s", __FUNCTION__, m_cecAdapter->ToString((cec_server_version)m_configuration.serverVersion));
286       SetVersionInfo(m_configuration);
287     }
288
289     m_bStarted = true;
290     Create();
291   }
292
293   return CPeripheral::InitialiseFeature(feature);
294 }
295
296 void CPeripheralCecAdapter::SetVersionInfo(const libcec_configuration &configuration)
297 {
298   m_strVersionInfo.Format("libCEC %s - firmware v%d", m_cecAdapter->ToString((cec_server_version)configuration.serverVersion), configuration.iFirmwareVersion);
299
300   // append firmware build date
301   if (configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN)
302   {
303     CDateTime dt((time_t)configuration.iFirmwareBuildDate);
304     m_strVersionInfo.AppendFormat(" (%s)", dt.GetAsDBDate().c_str());
305   }
306 }
307
308 bool CPeripheralCecAdapter::OpenConnection(void)
309 {
310   bool bIsOpen(false);
311
312   if (!GetSettingBool("enabled"))
313   {
314     CLog::Log(LOGDEBUG, "%s - CEC adapter is disabled in peripheral settings", __FUNCTION__);
315     m_bStarted = false;
316     return bIsOpen;
317   }
318   
319   // open the CEC adapter
320   CLog::Log(LOGDEBUG, "%s - opening a connection to the CEC adapter: %s", __FUNCTION__, m_strComPort.c_str());
321
322   // scanning the CEC bus takes about 5 seconds, so display a notification to inform users that we're busy
323   CStdString strMessage;
324   strMessage.Format(g_localizeStrings.Get(21336), g_localizeStrings.Get(36000));
325   CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(36000), strMessage);
326
327   bool bConnectionFailedDisplayed(false);
328
329   while (!m_bStop && !bIsOpen)
330   {
331     if ((bIsOpen = m_cecAdapter->Open(m_strComPort.c_str(), 10000)) == false)
332     {
333       // display warning: couldn't initialise libCEC
334       CLog::Log(LOGERROR, "%s - could not opening a connection to the CEC adapter", __FUNCTION__);
335       if (!bConnectionFailedDisplayed)
336         CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(36000), g_localizeStrings.Get(36012));
337       bConnectionFailedDisplayed = true;
338
339       Sleep(10000);
340     }
341   }
342
343   if (bIsOpen)
344   {
345     CLog::Log(LOGDEBUG, "%s - connection to the CEC adapter opened", __FUNCTION__);
346
347     // read the configuration
348     libcec_configuration config;
349     if (m_cecAdapter->GetCurrentConfiguration(&config))
350     {
351       // update the local configuration
352       CSingleLock lock(m_critSection);
353       SetConfigurationFromLibCEC(config);
354     }
355   }
356
357   return bIsOpen;
358 }
359
360 void CPeripheralCecAdapter::Process(void)
361 {
362   if (!OpenConnection())
363     return;
364
365   {
366     CSingleLock lock(m_critSection);
367     m_iExitCode = EXITCODE_QUIT;
368     m_bGoingToStandby = false;
369     m_bIsRunning = true;
370     m_bActiveSourceBeforeStandby = false;
371   }
372
373   CAnnouncementManager::AddAnnouncer(this);
374
375   m_queryThread = new CPeripheralCecAdapterUpdateThread(this, &m_configuration);
376   m_queryThread->Create(false);
377
378   while (!m_bStop)
379   {
380     if (!m_bStop)
381       ProcessVolumeChange();
382
383     if (!m_bStop)
384       ProcessActivateSource();
385
386     if (!m_bStop)
387       ProcessStandbyDevices();
388
389     if (!m_bStop)
390       Sleep(5);
391   }
392
393   m_queryThread->StopThread(true);
394
395   bool bSendStandbyCommands(false);
396   {
397     CSingleLock lock(m_critSection);
398     bSendStandbyCommands = m_iExitCode != EXITCODE_REBOOT &&
399                            m_iExitCode != EXITCODE_RESTARTAPP &&
400                            !m_bDeviceRemoved &&
401                            (!m_bGoingToStandby || GetSettingBool("standby_tv_on_pc_standby")) &&
402                            GetSettingBool("enabled");
403
404     if (m_bGoingToStandby)
405       m_bActiveSourceBeforeStandby = m_cecAdapter->IsLibCECActiveSource();
406   }
407
408   if (bSendStandbyCommands)
409   {
410     if (m_cecAdapter->IsLibCECActiveSource())
411     {
412       if (!m_configuration.powerOffDevices.IsEmpty())
413       {
414         CLog::Log(LOGDEBUG, "%s - sending standby commands", __FUNCTION__);
415         m_cecAdapter->StandbyDevices();
416       }
417       else if (m_configuration.bSendInactiveSource == 1)
418       {
419         CLog::Log(LOGDEBUG, "%s - sending inactive source commands", __FUNCTION__);
420         m_cecAdapter->SetInactiveView();
421       }
422     }
423     else
424     {
425       CLog::Log(LOGDEBUG, "%s - XBMC is not the active source, not sending any standby commands", __FUNCTION__);
426     }
427   }
428
429   m_cecAdapter->Close();
430
431   CLog::Log(LOGDEBUG, "%s - CEC adapter processor thread ended", __FUNCTION__);
432
433   {
434     CSingleLock lock(m_critSection);
435     m_bStarted = false;
436     m_bIsRunning = false;
437   }
438 }
439
440 bool CPeripheralCecAdapter::HasAudioControl(void)
441 {
442   CSingleLock lock(m_critSection);
443   return m_bHasConnectedAudioSystem;
444 }
445
446 void CPeripheralCecAdapter::SetAudioSystemConnected(bool bSetTo)
447 {
448   CSingleLock lock(m_critSection);
449   m_bHasConnectedAudioSystem = bSetTo;
450 }
451
452 void CPeripheralCecAdapter::ProcessVolumeChange(void)
453 {
454   bool bSendRelease(false);
455   CecVolumeChange pendingVolumeChange = VOLUME_CHANGE_NONE;
456   {
457     CSingleLock lock(m_critSection);
458     if (m_volumeChangeQueue.size() > 0)
459     {
460       /* get the first change from the queue */
461       pendingVolumeChange = m_volumeChangeQueue.front();
462       m_volumeChangeQueue.pop();
463
464       /* remove all dupe entries */
465       while (m_volumeChangeQueue.size() > 0 && m_volumeChangeQueue.front() == pendingVolumeChange)
466         m_volumeChangeQueue.pop();
467
468       /* send another keypress after VOLUME_REFRESH_TIMEOUT ms */
469       bool bRefresh(m_lastKeypress + VOLUME_REFRESH_TIMEOUT < XbmcThreads::SystemClockMillis());
470
471       /* only send the keypress when it hasn't been sent yet */
472       if (pendingVolumeChange != m_lastChange)
473       {
474         m_lastKeypress = XbmcThreads::SystemClockMillis();
475         m_lastChange = pendingVolumeChange;
476       }
477       else if (bRefresh)
478       {
479         m_lastKeypress = XbmcThreads::SystemClockMillis();
480         pendingVolumeChange = m_lastChange;
481       }
482       else
483         pendingVolumeChange = VOLUME_CHANGE_NONE;
484     }
485     else if (m_lastKeypress > 0 && m_lastKeypress + VOLUME_CHANGE_TIMEOUT < XbmcThreads::SystemClockMillis())
486     {
487       /* send a key release */
488       m_lastKeypress = 0;
489       bSendRelease = true;
490       m_lastChange = VOLUME_CHANGE_NONE;
491     }
492   }
493
494   switch (pendingVolumeChange)
495   {
496   case VOLUME_CHANGE_UP:
497     m_cecAdapter->SendKeypress(CECDEVICE_AUDIOSYSTEM, CEC_USER_CONTROL_CODE_VOLUME_UP, false);
498     break;
499   case VOLUME_CHANGE_DOWN:
500     m_cecAdapter->SendKeypress(CECDEVICE_AUDIOSYSTEM, CEC_USER_CONTROL_CODE_VOLUME_DOWN, false);
501     break;
502   case VOLUME_CHANGE_MUTE:
503     m_cecAdapter->SendKeypress(CECDEVICE_AUDIOSYSTEM, CEC_USER_CONTROL_CODE_MUTE, false);
504     {
505       CSingleLock lock(m_critSection);
506       m_bIsMuted = !m_bIsMuted;
507     }
508     break;
509   case VOLUME_CHANGE_NONE:
510     if (bSendRelease)
511       m_cecAdapter->SendKeyRelease(CECDEVICE_AUDIOSYSTEM, false);
512     break;
513   }
514 }
515
516 void CPeripheralCecAdapter::VolumeUp(void)
517 {
518   if (HasAudioControl())
519   {
520     CSingleLock lock(m_critSection);
521     m_volumeChangeQueue.push(VOLUME_CHANGE_UP);
522   }
523 }
524
525 void CPeripheralCecAdapter::VolumeDown(void)
526 {
527   if (HasAudioControl())
528   {
529     CSingleLock lock(m_critSection);
530     m_volumeChangeQueue.push(VOLUME_CHANGE_DOWN);
531   }
532 }
533
534 void CPeripheralCecAdapter::ToggleMute(void)
535 {
536   if (HasAudioControl())
537   {
538     CSingleLock lock(m_critSection);
539     m_volumeChangeQueue.push(VOLUME_CHANGE_MUTE);
540   }
541 }
542
543 bool CPeripheralCecAdapter::IsMuted(void)
544 {
545   if (HasAudioControl())
546   {
547     CSingleLock lock(m_critSection);
548     return m_bIsMuted;
549   }
550   return false;
551 }
552
553 void CPeripheralCecAdapter::SetMenuLanguage(const char *strLanguage)
554 {
555   if (m_strMenuLanguage.Equals(strLanguage))
556     return;
557
558   CStdString strGuiLanguage;
559
560   if (!strcmp(strLanguage, "bul"))
561     strGuiLanguage = "Bulgarian";
562   else if (!strcmp(strLanguage, "hrv"))
563     strGuiLanguage = "Croatian";
564   else if (!strcmp(strLanguage, "cze"))
565     strGuiLanguage = "Czech";
566   else if (!strcmp(strLanguage, "dan"))
567     strGuiLanguage = "Danish";
568   else if (!strcmp(strLanguage, "dut"))
569     strGuiLanguage = "Dutch";
570   else if (!strcmp(strLanguage, "eng"))
571     strGuiLanguage = "English";
572   else if (!strcmp(strLanguage, "fin"))
573     strGuiLanguage = "Finnish";
574   else if (!strcmp(strLanguage, "fre"))
575     strGuiLanguage = "French";
576   else if (!strcmp(strLanguage, "ger"))
577     strGuiLanguage = "German";
578   else if (!strcmp(strLanguage, "gre"))
579     strGuiLanguage = "Greek";
580   else if (!strcmp(strLanguage, "hun"))
581     strGuiLanguage = "Hungarian";
582   else if (!strcmp(strLanguage, "ita"))
583     strGuiLanguage = "Italian";
584   else if (!strcmp(strLanguage, "nor"))
585     strGuiLanguage = "Norwegian";
586   else if (!strcmp(strLanguage, "pol"))
587     strGuiLanguage = "Polish";
588   else if (!strcmp(strLanguage, "por"))
589     strGuiLanguage = "Portuguese";
590   else if (!strcmp(strLanguage, "rum"))
591     strGuiLanguage = "Romanian";
592   else if (!strcmp(strLanguage, "rus"))
593     strGuiLanguage = "Russian";
594   else if (!strcmp(strLanguage, "srp"))
595     strGuiLanguage = "Serbian";
596   else if (!strcmp(strLanguage, "slo"))
597     strGuiLanguage = "Slovenian";
598   else if (!strcmp(strLanguage, "spa"))
599     strGuiLanguage = "Spanish";
600   else if (!strcmp(strLanguage, "swe"))
601     strGuiLanguage = "Swedish";
602   else if (!strcmp(strLanguage, "tur"))
603     strGuiLanguage = "Turkish";
604
605   if (!strGuiLanguage.IsEmpty())
606   {
607     CApplicationMessenger::Get().SetGUILanguage(strGuiLanguage);
608     CLog::Log(LOGDEBUG, "%s - language set to '%s'", __FUNCTION__, strGuiLanguage.c_str());
609   }
610   else
611     CLog::Log(LOGWARNING, "%s - TV menu language set to unknown value '%s'", __FUNCTION__, strLanguage);
612 }
613
614 int CPeripheralCecAdapter::CecCommand(void *cbParam, const cec_command command)
615 {
616   CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
617   if (!adapter)
618     return 0;
619
620   if (adapter->m_bIsReady)
621   {
622     switch (command.opcode)
623     {
624     case CEC_OPCODE_STANDBY:
625       /* a device was put in standby mode */
626       if (command.initiator == CECDEVICE_TV &&
627           (adapter->m_configuration.bPowerOffOnStandby == 1 || adapter->m_configuration.bShutdownOnStandby == 1) &&
628           (!adapter->m_screensaverLastActivated.IsValid() || CDateTime::GetCurrentDateTime() - adapter->m_screensaverLastActivated > CDateTimeSpan(0, 0, 0, SCREENSAVER_TIMEOUT)))
629       {
630         adapter->m_bStarted = false;
631         if (adapter->m_configuration.bPowerOffOnStandby == 1)
632           CApplicationMessenger::Get().Suspend();
633         else if (adapter->m_configuration.bShutdownOnStandby == 1)
634           CApplicationMessenger::Get().Shutdown();
635       }
636       break;
637     case CEC_OPCODE_SET_MENU_LANGUAGE:
638       if (adapter->m_configuration.bUseTVMenuLanguage == 1 && command.initiator == CECDEVICE_TV && command.parameters.size == 3)
639       {
640         char strNewLanguage[4];
641         for (int iPtr = 0; iPtr < 3; iPtr++)
642           strNewLanguage[iPtr] = command.parameters[iPtr];
643         strNewLanguage[3] = 0;
644         adapter->SetMenuLanguage(strNewLanguage);
645       }
646       break;
647     case CEC_OPCODE_DECK_CONTROL:
648       if (command.initiator == CECDEVICE_TV &&
649           command.parameters.size == 1 &&
650           command.parameters[0] == CEC_DECK_CONTROL_MODE_STOP)
651       {
652         cec_keypress key;
653         key.duration = 500;
654         key.keycode = CEC_USER_CONTROL_CODE_STOP;
655         adapter->PushCecKeypress(key);
656       }
657       break;
658     case CEC_OPCODE_PLAY:
659       if (command.initiator == CECDEVICE_TV &&
660           command.parameters.size == 1)
661       {
662         if (command.parameters[0] == CEC_PLAY_MODE_PLAY_FORWARD)
663         {
664           cec_keypress key;
665           key.duration = 500;
666           key.keycode = CEC_USER_CONTROL_CODE_PLAY;
667           adapter->PushCecKeypress(key);
668         }
669         else if (command.parameters[0] == CEC_PLAY_MODE_PLAY_STILL)
670         {
671           cec_keypress key;
672           key.duration = 500;
673           key.keycode = CEC_USER_CONTROL_CODE_PAUSE;
674           adapter->PushCecKeypress(key);
675         }
676       }
677       break;
678     default:
679       break;
680     }
681   }
682   return 1;
683 }
684
685 int CPeripheralCecAdapter::CecConfiguration(void *cbParam, const libcec_configuration config)
686 {
687   CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
688   if (!adapter)
689     return 0;
690
691   CSingleLock lock(adapter->m_critSection);
692   adapter->SetConfigurationFromLibCEC(config);
693   return 1;
694 }
695
696 int CPeripheralCecAdapter::CecAlert(void *cbParam, const libcec_alert alert, const libcec_parameter data)
697 {
698   CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
699   if (!adapter)
700     return 0;
701
702   bool bReopenConnection(false);
703   int iAlertString(0);
704   switch (alert)
705   {
706   case CEC_ALERT_SERVICE_DEVICE:
707     iAlertString = 36027;
708     break;
709   case CEC_ALERT_CONNECTION_LOST:
710     iAlertString = 36030;
711     break;
712 #if defined(CEC_ALERT_PERMISSION_ERROR)
713   case CEC_ALERT_PERMISSION_ERROR:
714     bReopenConnection = true;
715     iAlertString = 36031;
716     break;
717   case CEC_ALERT_PORT_BUSY:
718     bReopenConnection = true;
719     iAlertString = 36032;
720     break;
721 #endif
722   default:
723     break;
724   }
725
726   // display the alert
727   if (iAlertString)
728   {
729     CStdString strLog(g_localizeStrings.Get(iAlertString));
730     if (data.paramType == CEC_PARAMETER_TYPE_STRING && data.paramData)
731       strLog.AppendFormat(" - %s", (const char *)data.paramData);
732     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(36000), strLog);
733   }
734
735   if (bReopenConnection)
736     adapter->ReopenConnection();
737
738   return 1;
739 }
740
741 int CPeripheralCecAdapter::CecKeyPress(void *cbParam, const cec_keypress key)
742 {
743   CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
744   if (!adapter)
745     return 0;
746
747   adapter->PushCecKeypress(key);
748   return 1;
749 }
750
751 void CPeripheralCecAdapter::GetNextKey(void)
752 {
753   CSingleLock lock(m_critSection);
754   m_bHasButton = false;
755   if (m_bIsReady)
756   {
757     vector<CecButtonPress>::iterator it = m_buttonQueue.begin();
758     if (it != m_buttonQueue.end())
759     {
760       m_currentButton = (*it);
761       m_buttonQueue.erase(it);
762       m_bHasButton = true;
763     }
764   }
765 }
766
767 void CPeripheralCecAdapter::PushCecKeypress(const CecButtonPress &key)
768 {
769   CLog::Log(LOGDEBUG, "%s - received key %2x duration %d", __FUNCTION__, key.iButton, key.iDuration);
770
771   CSingleLock lock(m_critSection);
772   if (key.iDuration > 0)
773   {
774     if (m_currentButton.iButton == key.iButton && m_currentButton.iDuration == 0)
775     {
776       // update the duration
777       if (m_bHasButton)
778         m_currentButton.iDuration = key.iDuration;
779       // ignore this one, since it's already been handled by xbmc
780       return;
781     }
782     // if we received a keypress with a duration set, try to find the same one without a duration set, and replace it
783     for (vector<CecButtonPress>::reverse_iterator it = m_buttonQueue.rbegin(); it != m_buttonQueue.rend(); it++)
784     {
785       if ((*it).iButton == key.iButton)
786       {
787         if ((*it).iDuration == 0)
788         {
789           // replace this entry
790           (*it).iDuration = key.iDuration;
791           return;
792         }
793         // add a new entry
794         break;
795       }
796     }
797   }
798
799   m_buttonQueue.push_back(key);
800 }
801
802 void CPeripheralCecAdapter::PushCecKeypress(const cec_keypress &key)
803 {
804   CecButtonPress xbmcKey;
805   xbmcKey.iDuration = key.duration;
806
807   switch (key.keycode)
808   {
809   case CEC_USER_CONTROL_CODE_SELECT:
810     xbmcKey.iButton = XINPUT_IR_REMOTE_SELECT;
811     PushCecKeypress(xbmcKey);
812     break;
813   case CEC_USER_CONTROL_CODE_UP:
814     xbmcKey.iButton = XINPUT_IR_REMOTE_UP;
815     PushCecKeypress(xbmcKey);
816     break;
817   case CEC_USER_CONTROL_CODE_DOWN:
818     xbmcKey.iButton = XINPUT_IR_REMOTE_DOWN;
819     PushCecKeypress(xbmcKey);
820     break;
821   case CEC_USER_CONTROL_CODE_LEFT:
822     xbmcKey.iButton = XINPUT_IR_REMOTE_LEFT;
823     PushCecKeypress(xbmcKey);
824     break;
825   case CEC_USER_CONTROL_CODE_LEFT_UP:
826     xbmcKey.iButton = XINPUT_IR_REMOTE_LEFT;
827     PushCecKeypress(xbmcKey);
828     xbmcKey.iButton = XINPUT_IR_REMOTE_UP;
829     PushCecKeypress(xbmcKey);
830     break;
831   case CEC_USER_CONTROL_CODE_LEFT_DOWN:
832     xbmcKey.iButton = XINPUT_IR_REMOTE_LEFT;
833     PushCecKeypress(xbmcKey);
834     xbmcKey.iButton = XINPUT_IR_REMOTE_DOWN;
835     PushCecKeypress(xbmcKey);
836     break;
837   case CEC_USER_CONTROL_CODE_RIGHT:
838     xbmcKey.iButton = XINPUT_IR_REMOTE_RIGHT;
839     PushCecKeypress(xbmcKey);
840     break;
841   case CEC_USER_CONTROL_CODE_RIGHT_UP:
842     xbmcKey.iButton = XINPUT_IR_REMOTE_RIGHT;
843     PushCecKeypress(xbmcKey);
844     xbmcKey.iButton = XINPUT_IR_REMOTE_UP;
845     PushCecKeypress(xbmcKey);
846     break;
847   case CEC_USER_CONTROL_CODE_RIGHT_DOWN:
848     xbmcKey.iButton = XINPUT_IR_REMOTE_RIGHT;
849     PushCecKeypress(xbmcKey);
850     xbmcKey.iButton = XINPUT_IR_REMOTE_DOWN;
851     PushCecKeypress(xbmcKey);
852     break;
853   case CEC_USER_CONTROL_CODE_SETUP_MENU:
854     xbmcKey.iButton = XINPUT_IR_REMOTE_TITLE;
855     PushCecKeypress(xbmcKey);
856     break;
857   case CEC_USER_CONTROL_CODE_CONTENTS_MENU:
858   case CEC_USER_CONTROL_CODE_FAVORITE_MENU:
859   case CEC_USER_CONTROL_CODE_ROOT_MENU:
860     xbmcKey.iButton = XINPUT_IR_REMOTE_MENU;
861     PushCecKeypress(xbmcKey);
862     break;
863   case CEC_USER_CONTROL_CODE_EXIT:
864     xbmcKey.iButton = XINPUT_IR_REMOTE_BACK;
865     PushCecKeypress(xbmcKey);
866     break;
867   case CEC_USER_CONTROL_CODE_ENTER:
868     xbmcKey.iButton = XINPUT_IR_REMOTE_ENTER;
869     PushCecKeypress(xbmcKey);
870     break;
871   case CEC_USER_CONTROL_CODE_CHANNEL_DOWN:
872     xbmcKey.iButton = XINPUT_IR_REMOTE_CHANNEL_MINUS;
873     PushCecKeypress(xbmcKey);
874     break;
875   case CEC_USER_CONTROL_CODE_CHANNEL_UP:
876     xbmcKey.iButton = XINPUT_IR_REMOTE_CHANNEL_PLUS;
877     PushCecKeypress(xbmcKey);
878     break;
879   case CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL:
880     xbmcKey.iButton = XINPUT_IR_REMOTE_TELETEXT;
881     PushCecKeypress(xbmcKey);
882     break;
883   case CEC_USER_CONTROL_CODE_SOUND_SELECT:
884     xbmcKey.iButton = XINPUT_IR_REMOTE_LANGUAGE;
885     PushCecKeypress(xbmcKey);
886     break;
887   case CEC_USER_CONTROL_CODE_POWER:
888   case CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION:
889   case CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION:
890     xbmcKey.iButton = XINPUT_IR_REMOTE_POWER;
891     PushCecKeypress(xbmcKey);
892     break;
893   case CEC_USER_CONTROL_CODE_VOLUME_UP:
894     xbmcKey.iButton = XINPUT_IR_REMOTE_VOLUME_PLUS;
895     PushCecKeypress(xbmcKey);
896     break;
897   case CEC_USER_CONTROL_CODE_VOLUME_DOWN:
898     xbmcKey.iButton = XINPUT_IR_REMOTE_VOLUME_MINUS;
899     PushCecKeypress(xbmcKey);
900     break;
901   case CEC_USER_CONTROL_CODE_MUTE:
902   case CEC_USER_CONTROL_CODE_MUTE_FUNCTION:
903   case CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION:
904     xbmcKey.iButton = XINPUT_IR_REMOTE_MUTE;
905     PushCecKeypress(xbmcKey);
906     break;
907   case CEC_USER_CONTROL_CODE_PLAY:
908     xbmcKey.iButton = XINPUT_IR_REMOTE_PLAY;
909     PushCecKeypress(xbmcKey);
910     break;
911   case CEC_USER_CONTROL_CODE_STOP:
912     xbmcKey.iButton = XINPUT_IR_REMOTE_STOP;
913     PushCecKeypress(xbmcKey);
914     break;
915   case CEC_USER_CONTROL_CODE_PAUSE:
916     xbmcKey.iButton = XINPUT_IR_REMOTE_PAUSE;
917     PushCecKeypress(xbmcKey);
918     break;
919   case CEC_USER_CONTROL_CODE_REWIND:
920     xbmcKey.iButton = XINPUT_IR_REMOTE_REVERSE;
921     PushCecKeypress(xbmcKey);
922     break;
923   case CEC_USER_CONTROL_CODE_FAST_FORWARD:
924     xbmcKey.iButton = XINPUT_IR_REMOTE_FORWARD;
925     PushCecKeypress(xbmcKey);
926     break;
927   case CEC_USER_CONTROL_CODE_NUMBER0:
928     xbmcKey.iButton = XINPUT_IR_REMOTE_0;
929     PushCecKeypress(xbmcKey);
930     break;
931   case CEC_USER_CONTROL_CODE_NUMBER1:
932     xbmcKey.iButton = XINPUT_IR_REMOTE_1;
933     PushCecKeypress(xbmcKey);
934     break;
935   case CEC_USER_CONTROL_CODE_NUMBER2:
936     xbmcKey.iButton = XINPUT_IR_REMOTE_2;
937     PushCecKeypress(xbmcKey);
938     break;
939   case CEC_USER_CONTROL_CODE_NUMBER3:
940     xbmcKey.iButton = XINPUT_IR_REMOTE_3;
941     PushCecKeypress(xbmcKey);
942     break;
943   case CEC_USER_CONTROL_CODE_NUMBER4:
944     xbmcKey.iButton = XINPUT_IR_REMOTE_4;
945     PushCecKeypress(xbmcKey);
946     break;
947   case CEC_USER_CONTROL_CODE_NUMBER5:
948     xbmcKey.iButton = XINPUT_IR_REMOTE_5;
949     PushCecKeypress(xbmcKey);
950     break;
951   case CEC_USER_CONTROL_CODE_NUMBER6:
952     xbmcKey.iButton = XINPUT_IR_REMOTE_6;
953     PushCecKeypress(xbmcKey);
954     break;
955   case CEC_USER_CONTROL_CODE_NUMBER7:
956     xbmcKey.iButton = XINPUT_IR_REMOTE_7;
957     PushCecKeypress(xbmcKey);
958     break;
959   case CEC_USER_CONTROL_CODE_NUMBER8:
960     xbmcKey.iButton = XINPUT_IR_REMOTE_8;
961     PushCecKeypress(xbmcKey);
962     break;
963   case CEC_USER_CONTROL_CODE_NUMBER9:
964     xbmcKey.iButton = XINPUT_IR_REMOTE_9;
965     PushCecKeypress(xbmcKey);
966     break;
967   case CEC_USER_CONTROL_CODE_RECORD:
968     xbmcKey.iButton = XINPUT_IR_REMOTE_RECORD;
969     PushCecKeypress(xbmcKey);
970     break;
971   case CEC_USER_CONTROL_CODE_CLEAR:
972     xbmcKey.iButton = XINPUT_IR_REMOTE_CLEAR;
973     PushCecKeypress(xbmcKey);
974     break;
975   case CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION:
976     xbmcKey.iButton = XINPUT_IR_REMOTE_INFO;
977     PushCecKeypress(xbmcKey);
978     break;
979   case CEC_USER_CONTROL_CODE_PAGE_UP:
980     xbmcKey.iButton = XINPUT_IR_REMOTE_CHANNEL_PLUS;
981     PushCecKeypress(xbmcKey);
982     break;
983   case CEC_USER_CONTROL_CODE_PAGE_DOWN:
984     xbmcKey.iButton = XINPUT_IR_REMOTE_CHANNEL_MINUS;
985     PushCecKeypress(xbmcKey);
986     break;
987   case CEC_USER_CONTROL_CODE_FORWARD:
988     xbmcKey.iButton = XINPUT_IR_REMOTE_SKIP_PLUS;
989     PushCecKeypress(xbmcKey);
990     break;
991   case CEC_USER_CONTROL_CODE_BACKWARD:
992     xbmcKey.iButton = XINPUT_IR_REMOTE_SKIP_MINUS;
993     PushCecKeypress(xbmcKey);
994     break;
995   case CEC_USER_CONTROL_CODE_F1_BLUE:
996     xbmcKey.iButton = XINPUT_IR_REMOTE_BLUE;
997     PushCecKeypress(xbmcKey);
998     break;
999   case CEC_USER_CONTROL_CODE_F2_RED:
1000     xbmcKey.iButton = XINPUT_IR_REMOTE_RED;
1001     PushCecKeypress(xbmcKey);
1002     break;
1003   case CEC_USER_CONTROL_CODE_F3_GREEN:
1004     xbmcKey.iButton = XINPUT_IR_REMOTE_GREEN;
1005     PushCecKeypress(xbmcKey);
1006     break;
1007   case CEC_USER_CONTROL_CODE_F4_YELLOW:
1008     xbmcKey.iButton = XINPUT_IR_REMOTE_YELLOW;
1009     PushCecKeypress(xbmcKey);
1010     break;
1011   case CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE:
1012     xbmcKey.iButton = XINPUT_IR_REMOTE_GUIDE;
1013     PushCecKeypress(xbmcKey);
1014     break;
1015   case CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST:
1016     xbmcKey.iButton = XINPUT_IR_REMOTE_LIVE_TV;
1017     PushCecKeypress(xbmcKey);
1018     break;
1019   case CEC_USER_CONTROL_CODE_NEXT_FAVORITE:
1020   case CEC_USER_CONTROL_CODE_DOT:
1021   case CEC_USER_CONTROL_CODE_AN_RETURN:
1022     xbmcKey.iButton = XINPUT_IR_REMOTE_TITLE; // context menu
1023     PushCecKeypress(xbmcKey);
1024     break;
1025   case CEC_USER_CONTROL_CODE_DATA:
1026     xbmcKey.iButton = XINPUT_IR_REMOTE_TELETEXT;
1027     PushCecKeypress(xbmcKey);
1028     break;
1029   case CEC_USER_CONTROL_CODE_SUB_PICTURE:
1030     xbmcKey.iButton = XINPUT_IR_REMOTE_SUBTITLE;
1031     PushCecKeypress(xbmcKey);
1032     break;
1033   case CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION:
1034   case CEC_USER_CONTROL_CODE_EJECT:
1035   case CEC_USER_CONTROL_CODE_INPUT_SELECT:
1036   case CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION:
1037   case CEC_USER_CONTROL_CODE_HELP:
1038   case CEC_USER_CONTROL_CODE_STOP_RECORD:
1039   case CEC_USER_CONTROL_CODE_PAUSE_RECORD:
1040   case CEC_USER_CONTROL_CODE_ANGLE:
1041   case CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND:
1042   case CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING:
1043   case CEC_USER_CONTROL_CODE_PLAY_FUNCTION:
1044   case CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION:
1045   case CEC_USER_CONTROL_CODE_RECORD_FUNCTION:
1046   case CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION:
1047   case CEC_USER_CONTROL_CODE_STOP_FUNCTION:
1048   case CEC_USER_CONTROL_CODE_TUNE_FUNCTION:
1049   case CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION:
1050   case CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION:
1051   case CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION:
1052   case CEC_USER_CONTROL_CODE_F5:
1053   case CEC_USER_CONTROL_CODE_UNKNOWN:
1054   default:
1055     break;
1056   }
1057 }
1058
1059 int CPeripheralCecAdapter::GetButton(void)
1060 {
1061   CSingleLock lock(m_critSection);
1062   if (!m_bHasButton)
1063     GetNextKey();
1064
1065   return m_bHasButton ? m_currentButton.iButton : 0;
1066 }
1067
1068 unsigned int CPeripheralCecAdapter::GetHoldTime(void)
1069 {
1070   CSingleLock lock(m_critSection);
1071   if (!m_bHasButton)
1072     GetNextKey();
1073
1074   return m_bHasButton ? m_currentButton.iDuration : 0;
1075 }
1076
1077 void CPeripheralCecAdapter::ResetButton(void)
1078 {
1079   CSingleLock lock(m_critSection);
1080   m_bHasButton = false;
1081
1082   // wait for the key release if the duration isn't 0
1083   if (m_currentButton.iDuration > 0)
1084   {
1085     m_currentButton.iButton   = 0;
1086     m_currentButton.iDuration = 0;
1087   }
1088 }
1089
1090 void CPeripheralCecAdapter::OnSettingChanged(const CStdString &strChangedSetting)
1091 {
1092   if (strChangedSetting.Equals("enabled"))
1093   {
1094     bool bEnabled(GetSettingBool("enabled"));
1095     if (!bEnabled && IsRunning())
1096     {
1097       CLog::Log(LOGDEBUG, "%s - closing the CEC connection", __FUNCTION__);
1098       StopThread(true);
1099     }
1100     else if (bEnabled && !IsRunning())
1101     {
1102       CLog::Log(LOGDEBUG, "%s - starting the CEC connection", __FUNCTION__);
1103       SetConfigurationFromSettings();
1104       InitialiseFeature(FEATURE_CEC);
1105     }
1106   }
1107   else if (IsRunning())
1108   {
1109     if (m_queryThread->IsRunning())
1110     {
1111       CLog::Log(LOGDEBUG, "%s - sending the updated configuration to libCEC", __FUNCTION__);
1112       SetConfigurationFromSettings();
1113       m_queryThread->UpdateConfiguration(&m_configuration);
1114     }
1115   }
1116   else
1117   {
1118     CLog::Log(LOGDEBUG, "%s - restarting the CEC connection", __FUNCTION__);
1119     SetConfigurationFromSettings();
1120     InitialiseFeature(FEATURE_CEC);
1121   }
1122 }
1123
1124 void CPeripheralCecAdapter::CecSourceActivated(void *cbParam, const CEC::cec_logical_address address, const uint8_t activated)
1125 {
1126   CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
1127   if (!adapter)
1128     return;
1129
1130   // wake up the screensaver, so the user doesn't switch to a black screen
1131   if (activated == 1)
1132     g_application.WakeUpScreenSaverAndDPMS();
1133
1134   if (adapter->GetSettingBool("pause_playback_on_deactivate"))
1135   {
1136     bool bShowingSlideshow = (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW);
1137     CGUIWindowSlideShow *pSlideShow = bShowingSlideshow ? (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW) : NULL;
1138     bool bPlayingAndDeactivated = activated == 0 && (
1139         (pSlideShow && pSlideShow->IsPlaying()) || g_application.IsPlaying());
1140     bool bPausedAndActivated = activated == 1 && adapter->m_bPlaybackPaused && (
1141         (pSlideShow && pSlideShow->IsPaused()) || g_application.IsPaused());
1142     if (bPlayingAndDeactivated)
1143       adapter->m_bPlaybackPaused = true;
1144     else if (bPausedAndActivated)
1145       adapter->m_bPlaybackPaused = false;
1146
1147     if (bPlayingAndDeactivated || bPausedAndActivated)
1148     {
1149       if (pSlideShow)
1150         // pause/resume slideshow
1151         pSlideShow->OnAction(CAction(ACTION_PAUSE));
1152       else
1153         // pause/resume player
1154         CApplicationMessenger::Get().MediaPause();
1155     }
1156   }
1157 }
1158
1159 int CPeripheralCecAdapter::CecLogMessage(void *cbParam, const cec_log_message message)
1160 {
1161   CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
1162   if (!adapter)
1163     return 0;
1164
1165   int iLevel = -1;
1166   switch (message.level)
1167   {
1168   case CEC_LOG_ERROR:
1169     iLevel = LOGERROR;
1170     break;
1171   case CEC_LOG_WARNING:
1172     iLevel = LOGWARNING;
1173     break;
1174   case CEC_LOG_NOTICE:
1175     iLevel = LOGDEBUG;
1176     break;
1177   case CEC_LOG_TRAFFIC:
1178   case CEC_LOG_DEBUG:
1179     iLevel = LOGDEBUG;
1180     break;
1181   default:
1182     break;
1183   }
1184
1185   if (iLevel >= 0)
1186     CLog::Log(iLevel, "%s - %s", __FUNCTION__, message.message);
1187
1188   return 1;
1189 }
1190
1191 void CPeripheralCecAdapter::SetConfigurationFromLibCEC(const CEC::libcec_configuration &config)
1192 {
1193   bool bChanged(false);
1194
1195   // set the primary device type
1196   m_configuration.deviceTypes.Clear();
1197   m_configuration.deviceTypes.Add(config.deviceTypes[0]);
1198
1199   // hide the "connected device" and "hdmi port number" settings when the PA was autodetected
1200   bool bPAAutoDetected(config.bAutodetectAddress == 1);
1201
1202   SetSettingVisible("connected_device", !bPAAutoDetected);
1203   SetSettingVisible("cec_hdmi_port", !bPAAutoDetected);
1204
1205   // set the connected device
1206   m_configuration.baseDevice = config.baseDevice;
1207   bChanged |= SetSetting("connected_device", config.baseDevice == CECDEVICE_AUDIOSYSTEM ? LOCALISED_ID_AVR : LOCALISED_ID_TV);
1208
1209   // set the HDMI port number
1210   m_configuration.iHDMIPort = config.iHDMIPort;
1211   bChanged |= SetSetting("cec_hdmi_port", config.iHDMIPort);
1212
1213   // set the physical address, when baseDevice or iHDMIPort are not set
1214   CStdString strPhysicalAddress("0");
1215   if (!bPAAutoDetected && (m_configuration.baseDevice == CECDEVICE_UNKNOWN ||
1216       m_configuration.iHDMIPort < CEC_MIN_HDMI_PORTNUMBER ||
1217       m_configuration.iHDMIPort > CEC_MAX_HDMI_PORTNUMBER))
1218   {
1219     m_configuration.iPhysicalAddress = config.iPhysicalAddress;
1220     strPhysicalAddress.Format("%x", config.iPhysicalAddress);
1221   }
1222   bChanged |= SetSetting("physical_address", strPhysicalAddress);
1223
1224   // set the devices to wake when starting
1225   m_configuration.wakeDevices = config.wakeDevices;
1226   bChanged |= WriteLogicalAddresses(config.wakeDevices, "wake_devices", "wake_devices_advanced");
1227
1228   // set the devices to power off when stopping
1229   m_configuration.powerOffDevices = config.powerOffDevices;
1230   bChanged |= WriteLogicalAddresses(config.powerOffDevices, "standby_devices", "standby_devices_advanced");
1231
1232   // set the boolean settings
1233   m_configuration.bUseTVMenuLanguage = config.bUseTVMenuLanguage;
1234   bChanged |= SetSetting("use_tv_menu_language", m_configuration.bUseTVMenuLanguage == 1);
1235
1236   m_configuration.bActivateSource = config.bActivateSource;
1237   bChanged |= SetSetting("activate_source", m_configuration.bActivateSource == 1);
1238
1239   m_configuration.bPowerOffScreensaver = config.bPowerOffScreensaver;
1240   bChanged |= SetSetting("cec_standby_screensaver", m_configuration.bPowerOffScreensaver == 1);
1241
1242   m_configuration.bPowerOnScreensaver = config.bPowerOnScreensaver;
1243   bChanged |= SetSetting("cec_wake_screensaver", m_configuration.bPowerOnScreensaver == 1);
1244
1245   m_configuration.bPowerOffOnStandby = config.bPowerOffOnStandby;
1246
1247   m_configuration.bSendInactiveSource = config.bSendInactiveSource;
1248   bChanged |= SetSetting("send_inactive_source", m_configuration.bSendInactiveSource == 1);
1249
1250   m_configuration.iFirmwareVersion = config.iFirmwareVersion;
1251   m_configuration.bShutdownOnStandby = config.bShutdownOnStandby;
1252
1253   memcpy(m_configuration.strDeviceLanguage, config.strDeviceLanguage, 3);
1254   m_configuration.iFirmwareBuildDate = config.iFirmwareBuildDate;
1255
1256   SetVersionInfo(m_configuration);
1257
1258   bChanged |= SetSetting("standby_pc_on_tv_standby",
1259              m_configuration.bPowerOffOnStandby == 1 ? 13011 :
1260              m_configuration.bShutdownOnStandby == 1 ? 13005 : 36028);
1261
1262   if (bChanged)
1263     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(36000), g_localizeStrings.Get(36023));
1264 }
1265
1266 void CPeripheralCecAdapter::SetConfigurationFromSettings(void)
1267 {
1268   // use the same client version as libCEC version
1269   m_configuration.clientVersion = CEC_CLIENT_VERSION_CURRENT;
1270
1271   // device name 'XBMC'
1272   snprintf(m_configuration.strDeviceName, 13, "%s", GetSettingString("device_name").c_str());
1273
1274   // set the primary device type
1275   m_configuration.deviceTypes.Clear();
1276   int iDeviceType = GetSettingInt("device_type");
1277   if (iDeviceType != (int)CEC_DEVICE_TYPE_RECORDING_DEVICE &&
1278       iDeviceType != (int)CEC_DEVICE_TYPE_PLAYBACK_DEVICE &&
1279       iDeviceType != (int)CEC_DEVICE_TYPE_TUNER)
1280     iDeviceType = (int)CEC_DEVICE_TYPE_RECORDING_DEVICE;
1281   m_configuration.deviceTypes.Add((cec_device_type)iDeviceType);
1282
1283   // always try to autodetect the address.
1284   // when the firmware supports this, it will override the physical address, connected device and hdmi port settings
1285   m_configuration.bAutodetectAddress = CEC_DEFAULT_SETTING_AUTODETECT_ADDRESS;
1286
1287   // set the physical address
1288   // when set, it will override the connected device and hdmi port settings
1289   CStdString strPhysicalAddress = GetSettingString("physical_address");
1290   int iPhysicalAddress;
1291   if (sscanf(strPhysicalAddress.c_str(), "%x", &iPhysicalAddress) &&
1292       iPhysicalAddress >= CEC_PHYSICAL_ADDRESS_TV &&
1293       iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS)
1294     m_configuration.iPhysicalAddress = iPhysicalAddress;
1295   else
1296     m_configuration.iPhysicalAddress = CEC_PHYSICAL_ADDRESS_TV;
1297
1298   // set the connected device
1299   int iConnectedDevice = GetSettingInt("connected_device");
1300   if (iConnectedDevice == LOCALISED_ID_AVR)
1301     m_configuration.baseDevice = CECDEVICE_AUDIOSYSTEM;
1302   else if (iConnectedDevice == LOCALISED_ID_TV)
1303     m_configuration.baseDevice = CECDEVICE_TV;
1304
1305   // set the HDMI port number
1306   int iHDMIPort = GetSettingInt("cec_hdmi_port");
1307   if (iHDMIPort >= CEC_MIN_HDMI_PORTNUMBER &&
1308       iHDMIPort <= CEC_MAX_HDMI_PORTNUMBER)
1309     m_configuration.iHDMIPort = iHDMIPort;
1310
1311   // set the tv vendor override
1312   int iVendor = GetSettingInt("tv_vendor");
1313   if (iVendor >= CEC_MAX_VENDORID &&
1314       iVendor <= CEC_MAX_VENDORID)
1315     m_configuration.tvVendor = iVendor;
1316
1317   // read the devices to wake when starting
1318   CStdString strWakeDevices = CStdString(GetSettingString("wake_devices_advanced")).Trim();
1319   m_configuration.wakeDevices.Clear();
1320   if (!strWakeDevices.IsEmpty())
1321     ReadLogicalAddresses(strWakeDevices, m_configuration.wakeDevices);
1322   else
1323     ReadLogicalAddresses(GetSettingInt("wake_devices"), m_configuration.wakeDevices);
1324
1325   // read the devices to power off when stopping
1326   CStdString strStandbyDevices = CStdString(GetSettingString("standby_devices_advanced")).Trim();
1327   m_configuration.powerOffDevices.Clear();
1328   if (!strStandbyDevices.IsEmpty())
1329     ReadLogicalAddresses(strStandbyDevices, m_configuration.powerOffDevices);
1330   else
1331     ReadLogicalAddresses(GetSettingInt("standby_devices"), m_configuration.powerOffDevices);
1332
1333   // read the boolean settings
1334   m_configuration.bUseTVMenuLanguage   = GetSettingBool("use_tv_menu_language") ? 1 : 0;
1335   m_configuration.bActivateSource      = GetSettingBool("activate_source") ? 1 : 0;
1336   m_configuration.bPowerOffScreensaver = GetSettingBool("cec_standby_screensaver") ? 1 : 0;
1337   m_configuration.bPowerOnScreensaver  = GetSettingBool("cec_wake_screensaver") ? 1 : 0;
1338   m_configuration.bSendInactiveSource  = GetSettingBool("send_inactive_source") ? 1 : 0;
1339
1340   // read the mutually exclusive boolean settings
1341   int iStandbyAction(GetSettingInt("standby_pc_on_tv_standby"));
1342   m_configuration.bPowerOffOnStandby = iStandbyAction == 13011 ? 1 : 0;
1343   m_configuration.bShutdownOnStandby = iStandbyAction == 13005 ? 1 : 0;
1344
1345   // double tap prevention timeout in ms
1346   m_configuration.iDoubleTapTimeoutMs = GetSettingInt("double_tap_timeout_ms");
1347 }
1348
1349 void CPeripheralCecAdapter::ReadLogicalAddresses(const CStdString &strString, cec_logical_addresses &addresses)
1350 {
1351   for (size_t iPtr = 0; iPtr < strString.size(); iPtr++)
1352   {
1353     CStdString strDevice = CStdString(strString.substr(iPtr, 1)).Trim();
1354     if (!strDevice.IsEmpty())
1355     {
1356       int iDevice(0);
1357       if (sscanf(strDevice.c_str(), "%x", &iDevice) == 1 && iDevice >= 0 && iDevice <= 0xF)
1358         addresses.Set((cec_logical_address)iDevice);
1359     }
1360   }
1361 }
1362
1363 void CPeripheralCecAdapter::ReadLogicalAddresses(int iLocalisedId, cec_logical_addresses &addresses)
1364 {
1365   addresses.Clear();
1366   switch (iLocalisedId)
1367   {
1368   case LOCALISED_ID_TV:
1369     addresses.Set(CECDEVICE_TV);
1370     break;
1371   case LOCALISED_ID_AVR:
1372     addresses.Set(CECDEVICE_AUDIOSYSTEM);
1373     break;
1374   case LOCALISED_ID_TV_AVR:
1375     addresses.Set(CECDEVICE_TV);
1376     addresses.Set(CECDEVICE_AUDIOSYSTEM);
1377     break;
1378   case LOCALISED_ID_NONE:
1379   default:
1380     break;
1381   }
1382 }
1383
1384 bool CPeripheralCecAdapter::WriteLogicalAddresses(const cec_logical_addresses& addresses, const string& strSettingName, const string& strAdvancedSettingName)
1385 {
1386   bool bChanged(false);
1387
1388   // only update the advanced setting if it was set by the user
1389   if (!GetSettingString(strAdvancedSettingName).IsEmpty())
1390   {
1391     CStdString strPowerOffDevices;
1392     for (unsigned int iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_BROADCAST; iPtr++)
1393       if (addresses[iPtr])
1394         strPowerOffDevices.AppendFormat(" %X", iPtr);
1395     bChanged = SetSetting(strAdvancedSettingName, strPowerOffDevices.Trim());
1396   }
1397
1398   int iSettingPowerOffDevices = LOCALISED_ID_NONE;
1399   if (addresses[CECDEVICE_TV] && addresses[CECDEVICE_AUDIOSYSTEM])
1400     iSettingPowerOffDevices = LOCALISED_ID_TV_AVR;
1401   else if (addresses[CECDEVICE_TV])
1402     iSettingPowerOffDevices = LOCALISED_ID_TV;
1403   else if (addresses[CECDEVICE_AUDIOSYSTEM])
1404     iSettingPowerOffDevices = LOCALISED_ID_AVR;
1405   return SetSetting(strSettingName, iSettingPowerOffDevices) || bChanged;
1406 }
1407
1408 CPeripheralCecAdapterUpdateThread::CPeripheralCecAdapterUpdateThread(CPeripheralCecAdapter *adapter, libcec_configuration *configuration) :
1409     CThread("CECAdapterUpdate"),
1410     m_adapter(adapter),
1411     m_configuration(*configuration),
1412     m_bNextConfigurationScheduled(false),
1413     m_bIsUpdating(true)
1414 {
1415   m_nextConfiguration.Clear();
1416   m_event.Reset();
1417 }
1418
1419 CPeripheralCecAdapterUpdateThread::~CPeripheralCecAdapterUpdateThread(void)
1420 {
1421   StopThread(false);
1422   m_event.Set();
1423   StopThread(true);
1424 }
1425
1426 void CPeripheralCecAdapterUpdateThread::Signal(void)
1427 {
1428   m_event.Set();
1429 }
1430
1431 bool CPeripheralCecAdapterUpdateThread::UpdateConfiguration(libcec_configuration *configuration)
1432 {
1433   CSingleLock lock(m_critSection);
1434   if (!configuration)
1435     return false;
1436
1437   if (m_bIsUpdating)
1438   {
1439     m_bNextConfigurationScheduled = true;
1440     m_nextConfiguration = *configuration;
1441   }
1442   else
1443   {
1444     m_configuration = *configuration;
1445     m_event.Set();
1446   }
1447   return true;
1448 }
1449
1450 bool CPeripheralCecAdapterUpdateThread::WaitReady(void)
1451 {
1452   // don't wait if we're not powering up anything
1453   if (m_configuration.wakeDevices.IsEmpty() && m_configuration.bActivateSource == 0)
1454     return true;
1455
1456   // wait for the TV if we're configured to become the active source.
1457   // wait for the first device in the wake list otherwise.
1458   cec_logical_address waitFor = (m_configuration.bActivateSource == 1) ?
1459       CECDEVICE_TV :
1460       m_configuration.wakeDevices.primary;
1461
1462   cec_power_status powerStatus(CEC_POWER_STATUS_UNKNOWN);
1463   bool bContinue(true);
1464   while (bContinue && !m_adapter->m_bStop && !m_bStop && powerStatus != CEC_POWER_STATUS_ON)
1465   {
1466     powerStatus = m_adapter->m_cecAdapter->GetDevicePowerStatus(waitFor);
1467     if (powerStatus != CEC_POWER_STATUS_ON)
1468       bContinue = !m_event.WaitMSec(1000);
1469   }
1470
1471   return powerStatus == CEC_POWER_STATUS_ON;
1472 }
1473
1474 void CPeripheralCecAdapterUpdateThread::UpdateMenuLanguage(void)
1475 {
1476   // request the menu language of the TV
1477   if (m_configuration.bUseTVMenuLanguage == 1)
1478   {
1479     CLog::Log(LOGDEBUG, "%s - requesting the menu language of the TV", __FUNCTION__);
1480     cec_menu_language language;
1481     if (m_adapter->m_cecAdapter->GetDeviceMenuLanguage(CECDEVICE_TV, &language))
1482       m_adapter->SetMenuLanguage(language.language);
1483     else
1484       CLog::Log(LOGDEBUG, "%s - unknown menu language", __FUNCTION__);
1485   }
1486   else
1487   {
1488     CLog::Log(LOGDEBUG, "%s - using TV menu language is disabled", __FUNCTION__);
1489   }
1490 }
1491
1492 CStdString CPeripheralCecAdapterUpdateThread::UpdateAudioSystemStatus(void)
1493 {
1494   CStdString strAmpName;
1495
1496   /* disable the mute setting when an amp is found, because the amp handles the mute setting and
1497        set PCM output to 100% */
1498   if (m_adapter->m_cecAdapter->IsActiveDeviceType(CEC_DEVICE_TYPE_AUDIO_SYSTEM))
1499   {
1500     // request the OSD name of the amp
1501     cec_osd_name ampName = m_adapter->m_cecAdapter->GetDeviceOSDName(CECDEVICE_AUDIOSYSTEM);
1502     CLog::Log(LOGDEBUG, "%s - CEC capable amplifier found (%s). volume will be controlled on the amp", __FUNCTION__, ampName.name);
1503     strAmpName.AppendFormat("%s", ampName.name);
1504
1505     // set amp present
1506     m_adapter->SetAudioSystemConnected(true);
1507     g_application.SetMute(false);
1508     g_application.SetVolume(VOLUME_MAXIMUM, false);
1509   }
1510   else
1511   {
1512     // set amp present
1513     CLog::Log(LOGDEBUG, "%s - no CEC capable amplifier found", __FUNCTION__);
1514     m_adapter->SetAudioSystemConnected(false);
1515   }
1516
1517   return strAmpName;
1518 }
1519
1520 bool CPeripheralCecAdapterUpdateThread::SetInitialConfiguration(void)
1521 {
1522   // the option to make XBMC the active source is set
1523   if (m_configuration.bActivateSource == 1)
1524     m_adapter->m_cecAdapter->SetActiveSource();
1525
1526   // devices to wake are set
1527   cec_logical_addresses tvOnly;
1528   tvOnly.Clear(); tvOnly.Set(CECDEVICE_TV);
1529   if (!m_configuration.wakeDevices.IsEmpty() && (m_configuration.wakeDevices != tvOnly || m_configuration.bActivateSource == 0))
1530     m_adapter->m_cecAdapter->PowerOnDevices(CECDEVICE_BROADCAST);
1531
1532   // wait until devices are powered up
1533   if (!WaitReady())
1534     return false;
1535
1536   UpdateMenuLanguage();
1537
1538   // request the OSD name of the TV
1539   CStdString strNotification;
1540   cec_osd_name tvName = m_adapter->m_cecAdapter->GetDeviceOSDName(CECDEVICE_TV);
1541   strNotification.Format("%s: %s", g_localizeStrings.Get(36016), tvName.name);
1542
1543   CStdString strAmpName = UpdateAudioSystemStatus();
1544   if (!strAmpName.empty())
1545     strNotification.AppendFormat("- %s", strAmpName.c_str());
1546
1547   m_adapter->m_bIsReady = true;
1548
1549   // and let the gui know that we're done
1550   CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(36000), strNotification);
1551
1552   CSingleLock lock(m_critSection);
1553   m_bIsUpdating = false;
1554   return true;
1555 }
1556
1557 bool CPeripheralCecAdapter::IsRunning(void) const
1558 {
1559   CSingleLock lock(m_critSection);
1560   return m_bIsRunning;
1561 }
1562
1563 void CPeripheralCecAdapterUpdateThread::Process(void)
1564 {
1565   // set the initial configuration
1566   if (!SetInitialConfiguration())
1567     return;
1568
1569   // and wait for updates
1570   bool bUpdate(false);
1571   while (!m_bStop)
1572   {
1573     // update received
1574     if (bUpdate || m_event.WaitMSec(500))
1575     {
1576       if (m_bStop)
1577         return;
1578       // set the new configuration
1579       libcec_configuration configuration;
1580       {
1581         CSingleLock lock(m_critSection);
1582         configuration = m_configuration;
1583         m_bIsUpdating = false;
1584       }
1585
1586       CLog::Log(LOGDEBUG, "%s - updating the configuration", __FUNCTION__);
1587       bool bConfigSet(m_adapter->m_cecAdapter->SetConfiguration(&configuration));
1588       // display message: config updated / failed to update
1589       if (!bConfigSet)
1590         CLog::Log(LOGERROR, "%s - libCEC couldn't set the new configuration", __FUNCTION__);
1591       else
1592       {
1593         UpdateMenuLanguage();
1594         UpdateAudioSystemStatus();
1595       }
1596
1597       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(36000), g_localizeStrings.Get(bConfigSet ? 36023 : 36024));
1598
1599       {
1600         CSingleLock lock(m_critSection);
1601         if ((bUpdate = m_bNextConfigurationScheduled) == true)
1602         {
1603           // another update is scheduled
1604           m_bNextConfigurationScheduled = false;
1605           m_configuration = m_nextConfiguration;
1606         }
1607         else
1608         {
1609           // nothing left to do, wait for updates
1610           m_bIsUpdating = false;
1611           m_event.Reset();
1612         }
1613       }
1614     }
1615   }
1616 }
1617
1618 void CPeripheralCecAdapter::OnDeviceRemoved(void)
1619 {
1620   CSingleLock lock(m_critSection);
1621   m_bDeviceRemoved = true;
1622 }
1623
1624 bool CPeripheralCecAdapter::ReopenConnection(void)
1625 {
1626   // stop running thread
1627   {
1628     CSingleLock lock(m_critSection);
1629     m_iExitCode = EXITCODE_RESTARTAPP;
1630     CAnnouncementManager::RemoveAnnouncer(this);
1631     StopThread(false);
1632   }
1633   StopThread();
1634
1635   // reset all members to their defaults
1636   ResetMembers();
1637
1638   // reopen the connection
1639   return InitialiseFeature(FEATURE_CEC);
1640 }
1641
1642 void CPeripheralCecAdapter::ActivateSource(void)
1643 {
1644   CSingleLock lock(m_critSection);
1645   m_bActiveSourcePending = true;
1646 }
1647
1648 void CPeripheralCecAdapter::ProcessActivateSource(void)
1649 {
1650   bool bActivate(false);
1651
1652   {
1653     CSingleLock lock(m_critSection);
1654     bActivate = m_bActiveSourcePending;
1655     m_bActiveSourcePending = false;
1656   }
1657
1658   if (bActivate)
1659     m_cecAdapter->SetActiveSource();
1660 }
1661
1662 void CPeripheralCecAdapter::StandbyDevices(void)
1663 {
1664   CSingleLock lock(m_critSection);
1665   m_bStandbyPending = true;
1666 }
1667
1668 void CPeripheralCecAdapter::ProcessStandbyDevices(void)
1669 {
1670   bool bStandby(false);
1671
1672   {
1673     CSingleLock lock(m_critSection);
1674     bStandby = m_bStandbyPending;
1675     m_bStandbyPending = false;
1676   }
1677
1678   if (bStandby)
1679     m_cecAdapter->StandbyDevices(CECDEVICE_BROADCAST);
1680 }
1681
1682 #endif