update the device status to CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON after sendin...
[deb_libcec.git] / src / lib / implementations / CECCommandHandler.cpp
index 8fc844fca9a4629055bf947d3dbfc96db2889c41..5398513ab52fa8a5d20ed949d041bcb882091422 100644 (file)
@@ -48,6 +48,7 @@ using namespace PLATFORM;
 
 #define LIB_CEC     m_busDevice->GetProcessor()->GetLib()
 #define ToString(p) CCECTypeUtils::ToString(p)
+#define REQUEST_POWER_STATUS_TIMEOUT 5000
 
 CCECCommandHandler::CCECCommandHandler(CCECBusDevice *busDevice,
                                        int32_t iTransmitTimeout /* = CEC_DEFAULT_TRANSMIT_TIMEOUT */,
@@ -62,7 +63,8 @@ CCECCommandHandler::CCECCommandHandler(CCECBusDevice *busDevice,
     m_bHandlerInited(false),
     m_bOPTSendDeckStatusUpdateOnActiveSource(false),
     m_vendorId(CEC_VENDOR_UNKNOWN),
-    m_iActiveSourcePending(iActiveSourcePending)
+    m_iActiveSourcePending(iActiveSourcePending),
+    m_iPowerStatusRequested(0)
 {
 }
 
@@ -673,9 +675,6 @@ int CCECCommandHandler::HandleUserControlPressed(const cec_command &command)
 
   CCECClient *client = device->GetClient();
   if (client)
-    client->AddKey();
-
-  if (command.parameters[0] <= CEC_USER_CONTROL_CODE_MAX)
     client->SetCurrentButton((cec_user_control_code) command.parameters[0]);
 
   if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER ||
@@ -818,7 +817,14 @@ bool CCECCommandHandler::TransmitImageViewOn(const cec_logical_address iInitiato
   cec_command command;
   cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_IMAGE_VIEW_ON);
 
-  return Transmit(command, false, false);
+  if (Transmit(command, false, false))
+  {
+    CCECBusDevice* dest = m_processor->GetDevice(iDestination);
+    if (dest && dest->GetCurrentPowerStatus() != CEC_POWER_STATUS_ON)
+      dest->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
+    return true;
+  }
+  return false;
 }
 
 bool CCECCommandHandler::TransmitStandby(const cec_logical_address iInitiator, const cec_logical_address iDestination)
@@ -871,6 +877,16 @@ bool CCECCommandHandler::TransmitRequestPhysicalAddress(const cec_logical_addres
 
 bool CCECCommandHandler::TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
 {
+  if (iDestination == CECDEVICE_TV)
+  {
+    int64_t now(GetTimeMs());
+    if (now - m_iPowerStatusRequested < REQUEST_POWER_STATUS_TIMEOUT)
+      return true;
+    m_iPowerStatusRequested = now;
+  }
+
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting power status of '%s' (%X)", m_busDevice->GetLogicalAddressName(), iDestination);
+
   cec_command command;
   cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_POWER_STATUS);
 
@@ -1080,13 +1096,32 @@ bool CCECCommandHandler::Transmit(cec_command &command, bool bSuppressWait, bool
     return bReturn;
   }
 
+  // check whether the destination is not marked as not present or handled by libCEC
+  if (command.destination != CECDEVICE_BROADCAST && command.opcode_set)
+  {
+    CCECBusDevice* destinationDevice = m_processor->GetDevice(command.destination);
+    cec_bus_device_status status = destinationDevice ? destinationDevice->GetStatus() : CEC_DEVICE_STATUS_NOT_PRESENT;
+    if (status == CEC_DEVICE_STATUS_NOT_PRESENT)
+    {
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "not sending command '%s': destination device '%s' marked as not present", ToString(command.opcode),ToString(command.destination));
+      return bReturn;
+    }
+    else if (status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC)
+    {
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "not sending command '%s': destination device '%s' marked as handled by libCEC", ToString(command.opcode),ToString(command.destination));
+      return bReturn;
+    }
+  }
+
   {
     uint8_t iTries(0), iMaxTries(!command.opcode_set ? 1 : m_iTransmitRetries + 1);
     while (!bReturn && ++iTries <= iMaxTries && !m_busDevice->IsUnsupportedFeature(command.opcode))
     {
       if ((bReturn = m_processor->Transmit(command, bIsReply)) == true)
       {
+#ifdef CEC_DEBUGGING
         LIB_CEC->AddLog(CEC_LOG_DEBUG, "command transmitted");
+#endif
         if (bExpectResponse)
         {
           bReturn = m_busDevice->WaitForOpcode(expectedResponse);
@@ -1112,21 +1147,33 @@ bool CCECCommandHandler::ActivateSource(bool bTransmitDelayedCommandsOnly /* = f
         if (m_iActiveSourcePending == 0 || GetTimeMs() < m_iActiveSourcePending)
           return false;
 
+#ifdef CEC_DEBUGGING
         LIB_CEC->AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command");
+#endif
       }
     }
 
     // update the power state and menu state
-    m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON);
-    m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED);
+    if (!bTransmitDelayedCommandsOnly)
+    {
+      m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON);
+      m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED);
+    }
 
     // vendor specific hook
     VendorPreActivateSourceHook();
 
     // power on the TV
+    CCECBusDevice* tv = m_processor->GetDevice(CECDEVICE_TV);
+    bool bTvPresent = (tv && tv->GetStatus() == CEC_DEVICE_STATUS_PRESENT);
     bool bActiveSourceFailed(false);
-    if (m_processor->GetDevice(CECDEVICE_TV)->GetPowerStatus(m_busDevice->GetLogicalAddress()) != CEC_POWER_STATUS_ON)
-      bActiveSourceFailed = !m_busDevice->TransmitImageViewOn();
+    if (bTvPresent)
+    {
+      if (tv->GetCurrentPowerStatus() != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON)
+        bActiveSourceFailed = !m_busDevice->TransmitImageViewOn();
+    }
+    else
+      LIB_CEC->AddLog(CEC_LOG_DEBUG, "TV not present, not sending 'image view on'");
 
     // check if we're allowed to switch sources
     bool bSourceSwitchAllowed = SourceSwitchAllowed();
@@ -1136,11 +1183,12 @@ bool CCECCommandHandler::ActivateSource(bool bTransmitDelayedCommandsOnly /* = f
     // switch sources (if allowed)
     if (!bActiveSourceFailed && bSourceSwitchAllowed)
     {
-      bActiveSourceFailed = !m_busDevice->TransmitActiveSource(false) ||
-                            !m_busDevice->TransmitMenuState(CECDEVICE_TV, false);
+      bActiveSourceFailed = !m_busDevice->TransmitActiveSource(false);
+      if (bTvPresent && !bActiveSourceFailed)
+        bActiveSourceFailed = !m_busDevice->TransmitMenuState(CECDEVICE_TV, false);
 
       // update the deck status for playback devices
-      if (!bActiveSourceFailed)
+      if (bTvPresent && !bActiveSourceFailed)
       {
         CCECPlaybackDevice *playbackDevice = m_busDevice->AsPlaybackDevice();
         if (playbackDevice && SendDeckStatusUpdateOnActiveSource())
@@ -1152,6 +1200,10 @@ bool CCECCommandHandler::ActivateSource(bool bTransmitDelayedCommandsOnly /* = f
     if (bActiveSourceFailed || !bSourceSwitchAllowed)
     {
       LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to make '%s' the active source. will retry later", m_busDevice->GetLogicalAddressName());
+      int64_t now(GetTimeMs());
+      CLockObject lock(m_mutex);
+      if (m_iActiveSourcePending == 0 || m_iActiveSourcePending < now)
+        m_iActiveSourcePending = now + (int64_t)CEC_ACTIVE_SOURCE_SWITCH_RETRY_TIME_MS;
       return false;
     }
     else