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