Fix typo in rejecting invalid keycodes
[deb_libcec.git] / src / lib / CECClient.cpp
index 0bbc081191652ba7b65a80bb88bd2070d5358e40..837214f456f54e724c29032664087064875874f9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libCEC(R) library.
  *
- * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited.  All rights reserved.
+ * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited.  All rights reserved.
  * libCEC(R) is an original work, containing original code.
  *
  * libCEC(R) is a trademark of Pulse-Eight Limited.
@@ -53,8 +53,11 @@ CCECClient::CCECClient(CCECProcessor *processor, const libcec_configuration &con
     m_bRegistered(false),
     m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN),
     m_buttontime(0),
-    m_iPreventForwardingPowerOffCommand(0)
+    m_iPreventForwardingPowerOffCommand(0),
+    m_iLastKeypressTime(0)
 {
+  m_lastKeypress.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
+  m_lastKeypress.duration = 0;
   m_configuration.Clear();
   // set the initial configuration
   SetConfiguration(configuration);
@@ -178,8 +181,18 @@ bool CCECClient::SetHDMIPort(const cec_logical_address iBaseDevice, const uint8_
   // set the default address when something went wrong
   if (!bReturn)
   {
-    LIB_CEC->AddLog(CEC_LOG_WARNING, "failed to set the physical address to %04X, setting it to the default value %04X", iPhysicalAddress, CEC_DEFAULT_PHYSICAL_ADDRESS);
-    iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS;
+    uint16_t iEepromAddress = m_processor->GetPhysicalAddressFromEeprom();
+    if (CLibCEC::IsValidPhysicalAddress(iEepromAddress))
+    {
+      LIB_CEC->AddLog(CEC_LOG_WARNING, "failed to set the physical address to %04X, setting it to the value that was persisted in the eeprom, %04X", iPhysicalAddress, iEepromAddress);
+      iPhysicalAddress = iEepromAddress;
+      bReturn = true;
+    }
+    else
+    {
+      LIB_CEC->AddLog(CEC_LOG_WARNING, "failed to set the physical address to %04X, setting it to the default value %04X", iPhysicalAddress, CEC_DEFAULT_PHYSICAL_ADDRESS);
+      iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS;
+    }
   }
 
   // and set the address
@@ -192,7 +205,7 @@ bool CCECClient::SetHDMIPort(const cec_logical_address iBaseDevice, const uint8_
 
 void CCECClient::ResetPhysicalAddress(void)
 {
-  SetPhysicalAddress(m_configuration);
+  SetPhysicalAddress(CEC_DEFAULT_PHYSICAL_ADDRESS);
 }
 
 void CCECClient::SetPhysicalAddress(const libcec_configuration &configuration)
@@ -454,7 +467,7 @@ bool CCECClient::SetLogicalAddress(const cec_logical_address iLogicalAddress)
 
   if (GetPrimaryLogicalAdddress() != iLogicalAddress)
   {
-    LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< setting primary logical address to %1x", iLogicalAddress);
+    LIB_CEC->AddLog(CEC_LOG_NOTICE, "setting primary logical address to %1x", iLogicalAddress);
     {
       CLockObject lock(m_mutex);
       m_configuration.logicalAddresses.primary = iLogicalAddress;
@@ -478,8 +491,8 @@ bool CCECClient::Transmit(const cec_command &data, bool bIsReply)
 
 bool CCECClient::SendPowerOnDevices(const cec_logical_address address /* = CECDEVICE_TV */)
 {
-  // if the broadcast address if set as destination and the client version >=1.5.0, read the wakeDevices setting
-  if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
+  // if the broadcast address if set as destination, read the wakeDevices setting
+  if (address == CECDEVICE_BROADCAST)
   {
     CECDEVICEVEC devices;
     m_processor->GetDevices()->GetWakeDevices(m_configuration, devices);
@@ -491,8 +504,8 @@ bool CCECClient::SendPowerOnDevices(const cec_logical_address address /* = CECDE
 
 bool CCECClient::SendStandbyDevices(const cec_logical_address address /* = CECDEVICE_BROADCAST */)
 {
-  // if the broadcast address if set as destination and the client version >=1.5.0, read the standbyDevices setting
-  if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
+  // if the broadcast address if set as destination, read the standbyDevices setting
+  if (address == CECDEVICE_BROADCAST)
   {
     CECDEVICEVEC devices;
     m_processor->GetDevices()->GetPowerOffDevices(m_configuration, devices);
@@ -732,6 +745,45 @@ uint8_t CCECClient::SendMuteAudio(void)
       (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
 }
 
+uint8_t CCECClient::AudioToggleMute(void)
+{
+  CCECBusDevice *device = GetPrimaryDevice();
+  CCECAudioSystem *audio = m_processor->GetAudioSystem();
+
+  return device && audio && audio->IsPresent() ?
+      audio->MuteAudio(device->GetLogicalAddress()) :
+      (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
+}
+
+uint8_t CCECClient::AudioMute(void)
+{
+  CCECBusDevice *device = GetPrimaryDevice();
+  CCECAudioSystem *audio = m_processor->GetAudioSystem();
+  uint8_t iStatus = device && audio && audio->IsPresent() ? audio->GetAudioStatus(device->GetLogicalAddress()) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
+  if ((iStatus & CEC_AUDIO_MUTE_STATUS_MASK) != CEC_AUDIO_MUTE_STATUS_MASK)
+    iStatus = audio->MuteAudio(device->GetLogicalAddress());
+
+  return iStatus;
+}
+
+uint8_t CCECClient::AudioUnmute(void)
+{
+  CCECBusDevice *device = GetPrimaryDevice();
+  CCECAudioSystem *audio = m_processor->GetAudioSystem();
+  uint8_t iStatus = device && audio && audio->IsPresent() ? audio->GetAudioStatus(device->GetLogicalAddress()) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
+  if ((iStatus & CEC_AUDIO_MUTE_STATUS_MASK) == CEC_AUDIO_MUTE_STATUS_MASK)
+    iStatus = audio->MuteAudio(device->GetLogicalAddress());
+
+  return iStatus;
+}
+
+uint8_t CCECClient::AudioStatus(void)
+{
+  CCECBusDevice *device = GetPrimaryDevice();
+  CCECAudioSystem *audio = m_processor->GetAudioSystem();
+  return device && audio && audio->IsPresent() ? audio->GetAudioStatus(device->GetLogicalAddress()) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
+}
+
 bool CCECClient::SendKeypress(const cec_logical_address iDestination, const cec_user_control_code key, bool bWait /* = true */)
 {
   CCECBusDevice *dest = m_processor->GetDevice(iDestination);
@@ -756,65 +808,39 @@ bool CCECClient::GetCurrentConfiguration(libcec_configuration &configuration)
 
   // client version 1.5.0
   snprintf(configuration.strDeviceName, 13, "%s", m_configuration.strDeviceName);
-  configuration.deviceTypes          = m_configuration.deviceTypes;
-  configuration.bAutodetectAddress   = m_configuration.bAutodetectAddress;
-  configuration.iPhysicalAddress     = m_configuration.iPhysicalAddress;
-  configuration.baseDevice           = m_configuration.baseDevice;
-  configuration.iHDMIPort            = m_configuration.iHDMIPort;
-  configuration.clientVersion        = m_configuration.clientVersion;
-  configuration.serverVersion        = m_configuration.serverVersion;
-  configuration.tvVendor             = m_configuration.tvVendor;
-
-  configuration.bGetSettingsFromROM  = m_configuration.bGetSettingsFromROM;
-  configuration.bUseTVMenuLanguage   = m_configuration.bUseTVMenuLanguage;
-  configuration.bActivateSource      = m_configuration.bActivateSource;
-  configuration.wakeDevices          = m_configuration.wakeDevices;
-  configuration.powerOffDevices      = m_configuration.powerOffDevices;
-  configuration.bPowerOffScreensaver = m_configuration.bPowerOffScreensaver;
-  configuration.bPowerOffOnStandby   = m_configuration.bPowerOffOnStandby;
-
-  // client version 1.5.1
-  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_1)
-    configuration.bSendInactiveSource = m_configuration.bSendInactiveSource;
-
-  // client version 1.5.3
-  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_3)
-    configuration.logicalAddresses    = m_configuration.logicalAddresses;
-
-  // client version 1.6.0
-  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0)
-  {
-    configuration.iFirmwareVersion          = m_configuration.iFirmwareVersion;
-    configuration.bPowerOffDevicesOnStandby = m_configuration.bPowerOffDevicesOnStandby;
-    configuration.bShutdownOnStandby        = m_configuration.bShutdownOnStandby;
-  }
-
-  // client version 1.6.2
-  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2)
-  {
-    memcpy(configuration.strDeviceLanguage, m_configuration.strDeviceLanguage, 3);
-    configuration.iFirmwareBuildDate      = m_configuration.iFirmwareBuildDate;
-  }
-
-  // client version 1.6.3
-  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3)
-  {
-    configuration.bMonitorOnly            = m_configuration.bMonitorOnly;
-  }
-
-  // client version 1.8.0
-  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_8_0)
-    configuration.cecVersion              = m_configuration.cecVersion;
-
-  // client version 1.8.2
-  if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_8_2)
-    configuration.adapterType             = m_configuration.adapterType;
+  configuration.deviceTypes               = m_configuration.deviceTypes;
+  configuration.bAutodetectAddress        = m_configuration.bAutodetectAddress;
+  configuration.iPhysicalAddress          = m_configuration.iPhysicalAddress;
+  configuration.baseDevice                = m_configuration.baseDevice;
+  configuration.iHDMIPort                 = m_configuration.iHDMIPort;
+  configuration.clientVersion             = m_configuration.clientVersion;
+  configuration.serverVersion             = m_configuration.serverVersion;
+  configuration.tvVendor                  = m_configuration.tvVendor;
+  configuration.bGetSettingsFromROM       = m_configuration.bGetSettingsFromROM;
+  configuration.bUseTVMenuLanguage        = m_configuration.bUseTVMenuLanguage;
+  configuration.bActivateSource           = m_configuration.bActivateSource;
+  configuration.wakeDevices               = m_configuration.wakeDevices;
+  configuration.powerOffDevices           = m_configuration.powerOffDevices;
+  configuration.bPowerOffScreensaver      = m_configuration.bPowerOffScreensaver;
+  configuration.bPowerOnScreensaver       = m_configuration.bPowerOnScreensaver;
+  configuration.bPowerOffOnStandby        = m_configuration.bPowerOffOnStandby;
+  configuration.bSendInactiveSource       = m_configuration.bSendInactiveSource;
+  configuration.logicalAddresses          = m_configuration.logicalAddresses;
+  configuration.iFirmwareVersion          = m_configuration.iFirmwareVersion;
+  configuration.bPowerOffDevicesOnStandby = m_configuration.bPowerOffDevicesOnStandby;
+  configuration.bShutdownOnStandby        = m_configuration.bShutdownOnStandby;
+  memcpy(configuration.strDeviceLanguage,  m_configuration.strDeviceLanguage, 3);
+  configuration.iFirmwareBuildDate        = m_configuration.iFirmwareBuildDate;
+  configuration.bMonitorOnly              = m_configuration.bMonitorOnly;
+  configuration.cecVersion                = m_configuration.cecVersion;
+  configuration.adapterType               = m_configuration.adapterType;
 
   return true;
 }
 
 bool CCECClient::SetConfiguration(const libcec_configuration &configuration)
 {
+  libcec_configuration defaultSettings;
   bool bIsRunning(m_processor && m_processor->CECInitialised());
   CCECBusDevice *primary = bIsRunning ? GetPrimaryDevice() : NULL;
   uint16_t iPA = primary ? primary->GetCurrentPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
@@ -836,48 +862,38 @@ bool CCECClient::SetConfiguration(const libcec_configuration &configuration)
   // just copy these
   {
     CLockObject lock(m_mutex);
-    m_configuration.bUseTVMenuLanguage   = configuration.bUseTVMenuLanguage;
-    m_configuration.bActivateSource      = configuration.bActivateSource;
-    m_configuration.bGetSettingsFromROM  = configuration.bGetSettingsFromROM;
-    m_configuration.wakeDevices          = configuration.wakeDevices;
-    m_configuration.powerOffDevices      = configuration.powerOffDevices;
-    m_configuration.bPowerOffScreensaver = configuration.bPowerOffScreensaver;
-    m_configuration.bPowerOffOnStandby   = configuration.bPowerOffOnStandby;
-
-    // client version 1.5.1
-    if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_1)
-      m_configuration.bSendInactiveSource = configuration.bSendInactiveSource;
-
-    // client version 1.6.0
-    if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0)
-    {
-      m_configuration.bPowerOffDevicesOnStandby = configuration.bPowerOffDevicesOnStandby;
-      m_configuration.bShutdownOnStandby        = configuration.bShutdownOnStandby;
-    }
-
-    // client version 1.6.2
-    if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2)
+    m_configuration.bUseTVMenuLanguage         = configuration.bUseTVMenuLanguage;
+    m_configuration.bActivateSource            = configuration.bActivateSource;
+    m_configuration.bGetSettingsFromROM        = configuration.bGetSettingsFromROM;
+    m_configuration.wakeDevices                = configuration.wakeDevices;
+    m_configuration.powerOffDevices            = configuration.powerOffDevices;
+    m_configuration.bPowerOffScreensaver       = configuration.bPowerOffScreensaver;
+    m_configuration.bPowerOffOnStandby         = configuration.bPowerOffOnStandby;
+    m_configuration.bSendInactiveSource        = configuration.bSendInactiveSource;
+    m_configuration.bPowerOffDevicesOnStandby  = configuration.bPowerOffDevicesOnStandby;
+    m_configuration.bShutdownOnStandby         = configuration.bShutdownOnStandby;
+    memcpy(m_configuration.strDeviceLanguage,   configuration.strDeviceLanguage, 3);
+    m_configuration.bMonitorOnly               = configuration.bMonitorOnly;
+    m_configuration.cecVersion                 = configuration.cecVersion;
+    m_configuration.adapterType                = configuration.adapterType;
+    m_configuration.iDoubleTapTimeoutMs        = configuration.iDoubleTapTimeoutMs;
+    m_configuration.deviceTypes.Add(configuration.deviceTypes[0]);
+
+    if (m_configuration.clientVersion >= CEC_CLIENT_VERSION_2_0_5)
     {
-      memcpy(m_configuration.strDeviceLanguage, configuration.strDeviceLanguage, 3);
+      m_configuration.comboKey           = configuration.comboKey;
+      m_configuration.iComboKeyTimeoutMs = configuration.iComboKeyTimeoutMs;
     }
-
-    // client version 1.6.3
-    if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3)
+    else
     {
-      m_configuration.bMonitorOnly = configuration.bMonitorOnly;
+      m_configuration.comboKey           = defaultSettings.comboKey;
+      m_configuration.iComboKeyTimeoutMs = defaultSettings.iComboKeyTimeoutMs;
     }
 
-    // client version 1.8.0
-    if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_8_0)
-      m_configuration.cecVersion   = configuration.cecVersion;
-
-    // client version 1.8.2
-    if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_8_2)
-      m_configuration.adapterType  = configuration.adapterType;
-
-    // ensure that there is at least 1 device type set
-    if (m_configuration.deviceTypes.IsEmpty())
-      m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
+    if (m_configuration.clientVersion >= CEC_CLIENT_VERSION_2_1_0)
+      m_configuration.bPowerOnScreensaver = configuration.bPowerOnScreensaver;
+    else
+      m_configuration.bPowerOnScreensaver = defaultSettings.bPowerOnScreensaver;
   }
 
   bool bNeedReinit(false);
@@ -935,18 +951,18 @@ void CCECClient::AddCommand(const cec_command &command)
 
   if (command.destination == CECDEVICE_BROADCAST || GetLogicalAddresses().IsSet(command.destination))
   {
-    LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s (%X) -> %s (%X): %s (%2X)", ToString(command.initiator), command.initiator, ToString(command.destination), command.destination, ToString(command.opcode), command.opcode);
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %s (%X) -> %s (%X): %s (%2X)", ToString(command.initiator), command.initiator, ToString(command.destination), command.destination, ToString(command.opcode), command.opcode);
     CallbackAddCommand(command);
   }
 }
 
 int CCECClient::MenuStateChanged(const cec_menu_state newState)
 {
-  LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s: %s", ToString(CEC_OPCODE_MENU_REQUEST), ToString(newState));
+  LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %s: %s", ToString(CEC_OPCODE_MENU_REQUEST), ToString(newState));
   return CallbackMenuStateChanged(newState);
 }
 
-void CCECClient::AddKey(void)
+void CCECClient::AddKey(bool bSendComboKey /* = false */)
 {
   cec_keypress key;
   key.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
@@ -956,10 +972,17 @@ void CCECClient::AddKey(void)
     if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN)
     {
       key.duration = (unsigned int) (GetTimeMs() - m_buttontime);
-      key.keycode = m_iCurrentButton;
 
-      m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
-      m_buttontime = 0;
+      if (key.duration > m_configuration.iComboKeyTimeoutMs ||
+          m_configuration.iComboKeyTimeoutMs == 0 ||
+          m_iCurrentButton != m_configuration.comboKey ||
+          bSendComboKey)
+      {
+        key.keycode = m_iCurrentButton;
+
+        m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
+        m_buttontime = 0;
+      }
     }
   }
 
@@ -972,19 +995,56 @@ void CCECClient::AddKey(void)
 
 void CCECClient::AddKey(const cec_keypress &key)
 {
-  // send back the previous key if there is one
-  AddKey();
+  if (key.keycode > CEC_USER_CONTROL_CODE_MAX ||
+      key.keycode < CEC_USER_CONTROL_CODE_SELECT)
+  {
+    // send back the previous key if there is one
+    AddKey();
+    return;
+  }
+
+  cec_keypress transmitKey(key);
+  cec_user_control_code comboKey(m_configuration.clientVersion >= CEC_CLIENT_VERSION_2_0_5 ?
+      m_configuration.comboKey : CEC_USER_CONTROL_CODE_STOP);
 
   {
     CLockObject lock(m_mutex);
-    m_iCurrentButton = key.duration > 0 || key.keycode > CEC_USER_CONTROL_CODE_MAX ?
-        CEC_USER_CONTROL_CODE_UNKNOWN :
-        key.keycode;
-    m_buttontime = key.duration > 0 ? 0 : GetTimeMs();
+    if (m_configuration.iComboKeyTimeoutMs > 0 && m_iCurrentButton == comboKey && key.duration == 0)
+    {
+      // stop + ok -> exit
+      if (key.keycode == CEC_USER_CONTROL_CODE_SELECT)
+        transmitKey.keycode = CEC_USER_CONTROL_CODE_EXIT;
+      // stop + pause -> root menu
+      else if (key.keycode == CEC_USER_CONTROL_CODE_PAUSE)
+        transmitKey.keycode = CEC_USER_CONTROL_CODE_ROOT_MENU;
+      // stop + play -> dot (which is handled as context menu in xbmc)
+      else if (key.keycode == CEC_USER_CONTROL_CODE_PLAY)
+        transmitKey.keycode = CEC_USER_CONTROL_CODE_DOT;
+      // default, send back the previous key
+      else
+        AddKey(true);
+    }
+
+    if (m_iCurrentButton == key.keycode)
+    {
+      m_buttontime = GetTimeMs();
+    }
+    else
+    {
+      AddKey();
+      if (key.duration == 0)
+      {
+        m_iCurrentButton = transmitKey.keycode;
+        m_buttontime = m_iCurrentButton == CEC_USER_CONTROL_CODE_UNKNOWN || key.duration > 0 ? 0 : GetTimeMs();
+      }
+    }
   }
 
-  LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %s (%1x)", ToString(key.keycode), key.keycode);
-  CallbackAddKey(key);
+  if (key.keycode != comboKey || key.duration > 0)
+  {
+    LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %s (%1x)", ToString(transmitKey.keycode), transmitKey.keycode);
+    CallbackAddKey(transmitKey);
+  }
 }
 
 void CCECClient::SetCurrentButton(const cec_user_control_code iButtonCode)
@@ -1004,9 +1064,14 @@ void CCECClient::CheckKeypressTimeout(void)
   {
     CLockObject lock(m_mutex);
     uint64_t iNow = GetTimeMs();
+    cec_user_control_code comboKey(m_configuration.clientVersion >= CEC_CLIENT_VERSION_2_0_5 ?
+        m_configuration.comboKey : CEC_USER_CONTROL_CODE_STOP);
+    uint32_t iTimeoutMs(m_configuration.clientVersion >= CEC_CLIENT_VERSION_2_0_5 ?
+        m_configuration.iComboKeyTimeoutMs : CEC_DEFAULT_COMBO_TIMEOUT_MS);
 
     if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN &&
-        iNow - m_buttontime > CEC_BUTTON_TIMEOUT)
+          ((m_iCurrentButton == comboKey && iTimeoutMs > 0 && iNow - m_buttontime > iTimeoutMs) ||
+          (m_iCurrentButton != comboKey && iNow - m_buttontime > CEC_BUTTON_TIMEOUT)))
     {
       key.duration = (unsigned int) (iNow - m_buttontime);
       key.keycode = m_iCurrentButton;
@@ -1037,21 +1102,6 @@ bool CCECClient::PingAdapter(void)
   return m_processor ? m_processor->PingAdapter() : false;
 }
 
-bool CCECClient::GetNextLogMessage(cec_log_message *message)
-{
-  return (m_logBuffer.Pop(*message));
-}
-
-bool CCECClient::GetNextKeypress(cec_keypress *key)
-{
-  return m_keyBuffer.Pop(*key);
-}
-
-bool CCECClient::GetNextCommand(cec_command *command)
-{
-  return m_commandBuffer.Pop(*command);
-}
-
 std::string CCECClient::GetConnectionInfo(void)
 {
   CStdString strLog;
@@ -1060,7 +1110,7 @@ std::string CCECClient::GetConnectionInfo(void)
   {
     time_t buildTime = (time_t)m_configuration.iFirmwareBuildDate;
     strLog.AppendFormat(", firmware build date: %s", asctime(gmtime(&buildTime)));
-    strLog = strLog.Left((int)strLog.length() - 1); // strip \n added by asctime
+    strLog = strLog.substr(0, strLog.length() > 0 ? (size_t)(strLog.length() - 1) : 0); // strip \n added by asctime
     strLog.append(" +0000");
   }
 
@@ -1076,8 +1126,8 @@ std::string CCECClient::GetConnectionInfo(void)
 
   if (!CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
     strLog.AppendFormat(", base device: %s (%X), HDMI port number: %d", ToString(m_configuration.baseDevice), m_configuration.baseDevice, m_configuration.iHDMIPort);
-  else
-    strLog.AppendFormat(", physical address: %04x", m_configuration.iPhysicalAddress);
+  uint16_t iPhysicalAddress = GetPrimaryDevice()->GetPhysicalAddress(GetLogicalAddresses().primary, false);
+  strLog.AppendFormat(", physical address: %x.%x.%x.%x", (iPhysicalAddress >> 12) & 0xF, (iPhysicalAddress >> 8) & 0xF, (iPhysicalAddress >> 4) & 0xF, iPhysicalAddress & 0xF);
 
   strLog.AppendFormat(", %s", LIB_CEC->GetLibInfo());
 
@@ -1284,7 +1334,7 @@ bool CCECClient::PollDevice(const cec_logical_address iAddress)
   CCECBusDevice *primary = GetPrimaryDevice();
   // poll the destination, with the primary as source
   if (primary)
-    return primary->TransmitPoll(iAddress, false);
+    return primary->TransmitPoll(iAddress, true);
 
   return m_processor ? m_processor->PollDevice(iAddress) : false;
 }
@@ -1383,7 +1433,7 @@ bool CCECClient::IsLibCECActiveSource(void)
     cec_logical_address activeSource = m_processor->GetActiveSource();
     CCECBusDevice *device = m_processor->GetDevice(activeSource);
     if (device)
-      bReturn = device->IsHandledByLibCEC();
+      bReturn = device->IsHandledByLibCEC() && !device->GetHandler()->ActiveSourcePending();
   }
   return bReturn;
 }
@@ -1402,48 +1452,42 @@ void CCECClient::SourceDeactivated(const cec_logical_address logicalAddress)
 
 void CCECClient::CallbackAddCommand(const cec_command &command)
 {
-  {
-    CLockObject lock(m_cbMutex);
-    if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand)
-    {
-      m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command);
-      return;
-    }
-  }
-  m_commandBuffer.Push(command);
+  CLockObject lock(m_cbMutex);
+  if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand)
+    m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command);
 }
 
 void CCECClient::CallbackAddKey(const cec_keypress &key)
 {
+  CLockObject lock(m_cbMutex);
+  if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
   {
-    CLockObject lock(m_cbMutex);
-    if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
+    // prevent double taps
+    int64_t now = GetTimeMs();
+    if (m_lastKeypress.keycode != key.keycode ||
+        key.duration > 0 ||
+        now - m_iLastKeypressTime >= m_configuration.iDoubleTapTimeoutMs)
     {
+      // no double tap
+      if (key.duration == 0)
+        m_iLastKeypressTime = now;
+      m_lastKeypress = key;
       m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
-      return;
     }
   }
-  m_keyBuffer.Push(key);
 }
 
 void CCECClient::CallbackAddLog(const cec_log_message &message)
 {
-  {
-    CLockObject lock(m_cbMutex);
-    if (m_configuration.callbacks && m_configuration.callbacks->CBCecLogMessage)
-    {
-      m_configuration.callbacks->CBCecLogMessage(m_configuration.callbackParam, message);
-      return;
-    }
-  }
-  m_logBuffer.Push(message);
+  CLockObject lock(m_cbMutex);
+  if (m_configuration.callbacks && m_configuration.callbacks->CBCecLogMessage)
+    m_configuration.callbacks->CBCecLogMessage(m_configuration.callbackParam, message);
 }
 
 void CCECClient::CallbackConfigurationChanged(const libcec_configuration &config)
 {
   CLockObject lock(m_cbMutex);
   if (m_configuration.callbacks &&
-      m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0 &&
       m_configuration.callbacks->CBCecConfigurationChanged &&
       m_processor->CECInitialised())
     m_configuration.callbacks->CBCecConfigurationChanged(m_configuration.callbackParam, config);
@@ -1453,7 +1497,6 @@ void CCECClient::CallbackSourceActivated(bool bActivated, const cec_logical_addr
 {
   CLockObject lock(m_cbMutex);
   if (m_configuration.callbacks &&
-      m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_7_1 &&
       m_configuration.callbacks->CBCecSourceActivated)
     m_configuration.callbacks->CBCecSourceActivated(m_configuration.callbackParam, logicalAddress, bActivated ? 1 : 0);
 }
@@ -1462,7 +1505,6 @@ void CCECClient::CallbackAlert(const libcec_alert type, const libcec_parameter &
 {
   CLockObject lock(m_cbMutex);
   if (m_configuration.callbacks &&
-      m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0 &&
       m_configuration.callbacks->CBCecAlert)
     m_configuration.callbacks->CBCecAlert(m_configuration.callbackParam, type, param);
 }
@@ -1471,7 +1513,6 @@ int CCECClient::CallbackMenuStateChanged(const cec_menu_state newState)
 {
   CLockObject lock(m_cbMutex);
   if (m_configuration.callbacks &&
-      m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2 &&
       m_configuration.callbacks->CBCecMenuStateChanged)
     return m_configuration.callbacks->CBCecMenuStateChanged(m_configuration.callbackParam, newState);
   return 0;