#define LIB_CEC m_processor->GetLib()
#define ToString(x) CCECTypeUtils::ToString(x)
+#define COMBO_KEY CEC_USER_CONTROL_CODE_STOP
+#define COMBO_TIMEOUT_MS 1000
+
CCECClient::CCECClient(CCECProcessor *processor, const libcec_configuration &configuration) :
m_processor(processor),
m_bInitialised(false),
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);
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;
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;
+
return true;
}
m_configuration.bMonitorOnly = configuration.bMonitorOnly;
}
+ // 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 (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;
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 > COMBO_TIMEOUT_MS || m_iCurrentButton != COMBO_KEY || bSendComboKey)
+ {
+ key.keycode = m_iCurrentButton;
+
+ m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
+ m_buttontime = 0;
+ }
}
}
void CCECClient::AddKey(const cec_keypress &key)
{
+ // send back the previous key if there is one
+ AddKey();
+
+ cec_keypress transmitKey(key);
+
{
CLockObject lock(m_mutex);
- m_iCurrentButton = key.duration > 0 ? CEC_USER_CONTROL_CODE_UNKNOWN : key.keycode;
- m_buttontime = key.duration > 0 ? 0 : GetTimeMs();
+ if (key.duration > 0 || key.keycode > CEC_USER_CONTROL_CODE_MAX)
+ {
+ transmitKey.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
+ }
+ else if (m_iCurrentButton == COMBO_KEY)
+ {
+ // 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_ROOT_MENU)
+ 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);
+ }
+
+ 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);
+ 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)
uint64_t iNow = GetTimeMs();
if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN &&
- iNow - m_buttontime > CEC_BUTTON_TIMEOUT)
+ ((m_iCurrentButton == COMBO_KEY && iNow - m_buttontime > COMBO_TIMEOUT_MS) ||
+ (m_iCurrentButton != COMBO_KEY && iNow - m_buttontime > CEC_BUTTON_TIMEOUT)))
{
key.duration = (unsigned int) (iNow - m_buttontime);
key.keycode = m_iCurrentButton;
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;
{
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");
}
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 >= CEC_DOUBLE_TAP_TIMEOUT_MS)
{
+ // 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)