Guest User

PeripheralCecAdapter.cpp

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