+/*
+ * 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 an original work, containing original code.
+ *
+ * libCEC(R) is a trademark of Pulse-Eight Limited.
+ *
+ * This program is dual-licensed; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * Alternatively, you can license this library under a commercial license,
+ * please contact Pulse-Eight Licensing for more information.
+ *
+ * For more information contact:
+ * Pulse-Eight Licensing <license@pulse-eight.com>
+ * http://www.pulse-eight.com/
+ * http://www.pulse-eight.net/
+ */
+
+#include "../../include/cec.h"
+
+#include <cstdio>
+#include <fcntl.h>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+#include "../lib/platform/threads/mutex.h"
+#include "../lib/platform/util/timeutils.h"
+#include "../lib/implementations/CECCommandHandler.h"
+
+using namespace CEC;
+using namespace std;
+using namespace PLATFORM;
+
+#include <cecloader.h>
+
+CMutex g_outputMutex;
+
+CMutex g_responseMutex;
+CCondition g_responseCondtion;
+cec_opcode g_lastCommand = CEC_OPCODE_NONE;
+
+CMutex g_keyMutex;
+CCondition g_keyCondtion;
+cec_user_control_code g_lastKey = CEC_USER_CONTROL_CODE_UNKNOWN;
+
+ICECCallbacks g_callbacks;
+ICECAdapter * g_parser;
+cec_logical_address g_primaryAddress;
+
+inline void PrintToStdOut(const char *strFormat, ...)
+{
+ CStdString strLog;
+
+ va_list argList;
+ va_start(argList, strFormat);
+ strLog.FormatV(strFormat, argList);
+ va_end(argList);
+
+ CLockObject lock(g_outputMutex);
+ cout << strLog << endl;
+}
+
+//get the first word (separated by whitespace) from string data and place that in word
+//then remove that word from string data
+bool GetWord(string& data, string& word)
+{
+ stringstream datastream(data);
+ string end;
+
+ datastream >> word;
+ if (datastream.fail())
+ {
+ data.clear();
+ return false;
+ }
+
+ size_t pos = data.find(word) + word.length();
+
+ if (pos >= data.length())
+ {
+ data.clear();
+ return true;
+ }
+
+ data = data.substr(pos);
+
+ datastream.clear();
+ datastream.str(data);
+
+ datastream >> end;
+ if (datastream.fail())
+ data.clear();
+
+ return true;
+}
+
+int CecLogMessage(void *UNUSED(cbParam), const cec_log_message &message)
+{
+ switch (message.level)
+ {
+ case CEC_LOG_ERROR:
+ PrintToStdOut("ERROR:\t%s", message.message);
+ break;
+ case CEC_LOG_WARNING:
+ PrintToStdOut("ERROR:\t%s", message.message);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int CecKeyPress(void *UNUSED(cbParam), const cec_keypress &key)
+{
+ CLockObject lock(g_keyMutex);
+ g_lastKey = key.keycode;
+ g_keyCondtion.Signal();
+ return 0;
+}
+
+int CecCommand(void *UNUSED(cbParam), const cec_command &command)
+{
+ CLockObject lock(g_responseMutex);
+ g_lastCommand = command.opcode;
+ g_responseCondtion.Signal();
+ return 0;
+}
+
+void EnableCallbacks(ICECAdapter *adapter)
+{
+ g_callbacks.CBCecLogMessage = &CecLogMessage;
+ g_callbacks.CBCecKeyPress = &CecKeyPress;
+ g_callbacks.CBCecCommand = &CecCommand;
+ adapter->EnableCallbacks(NULL, &g_callbacks);
+}
+
+ICECAdapter *CreateParser(cec_device_type_list typeList)
+{
+ ICECAdapter *parser = LibCecInit("ButtonConfig", typeList);
+ if (!parser)
+ {
+ #ifdef __WINDOWS__
+ PrintToStdOut("Cannot load libcec.dll");
+ #else
+ PrintToStdOut("Cannot load libcec.so");
+ #endif
+ return NULL;
+ }
+
+ PrintToStdOut("CEC Parser created - libcec version %d.%d", parser->GetLibVersionMajor(), parser->GetLibVersionMinor());
+
+ return parser;
+}
+
+bool ProcessConsoleCommand(string &input)
+{
+ if (!input.empty())
+ {
+ string command;
+ if (GetWord(input, command))
+ {
+ if (command == "q" || command == "quit")
+ return false;
+ }
+ }
+ return true;
+}
+
+bool OpenConnection(cec_device_type type = CEC_DEVICE_TYPE_RECORDING_DEVICE)
+{
+ cec_device_type_list types;
+ types.clear();
+ types.add(type);
+
+ g_parser = CreateParser(types);
+ if (!g_parser)
+ return false;
+
+ CStdString strPort;
+ cec_adapter devices[10];
+ uint8_t iDevicesFound = g_parser->FindAdapters(devices, 10, NULL);
+ if (iDevicesFound <= 0)
+ {
+ PrintToStdOut("autodetect FAILED");
+ UnloadLibCec(g_parser);
+ return false;
+ }
+ else
+ {
+ strPort = devices[0].comm;
+ }
+
+ EnableCallbacks(g_parser);
+
+ // start with HDMI1 on the TV
+ g_parser->SetHDMIPort(CECDEVICE_TV, 1);
+ PrintToStdOut("opening a connection to the CEC adapter...");
+
+ if (!g_parser->Open(strPort.c_str()))
+ {
+ PrintToStdOut("unable to open the device on port %s", strPort.c_str());
+ UnloadLibCec(g_parser);
+ return false;
+ }
+
+ cec_logical_addresses addr = g_parser->GetLogicalAddresses();
+ g_primaryAddress = addr.primary;
+
+ PrintToStdOut("cec device opened. using logical address %X", g_primaryAddress);
+ return true;
+}
+
+int8_t FindPhysicalAddressPortNumber(void)
+{
+ PrintToStdOut("Enter the HDMI port number to which you connected your CEC adapter, followed by <enter>. Only port 1, 2, 3 or 4 are supported. Anything else will cancel this wizard.");
+ string input;
+ getline(cin, input);
+ cin.clear();
+ if (input.empty() || (input != "1" && input != "2" && input != "3" && input != "4"))
+ return -1;
+ return (int8_t)atoi(input.c_str());
+}
+
+cec_logical_address FindPhysicalAddressBaseDevice(void)
+{
+ PrintToStdOut("Press 1 of your CEC adapter is connected to your TV or\npress 2 if it's connected to an AVR, followed by <enter>. Anything else will cancel this wizard.");
+
+ string input;
+ getline(cin, input);
+ cin.clear();
+ if (input.empty() || (input != "1" && input != "2"))
+ {
+ PrintToStdOut("Exiting...");
+ return CECDEVICE_UNKNOWN;
+ }
+ return (input == "2") ?
+ CECDEVICE_AUDIOSYSTEM :
+ CECDEVICE_TV;
+}
+
+uint16_t FindPhysicalAddress(void)
+{
+ PrintToStdOut("=== Physical Address Configuration ===\n");
+ uint16_t iAddress(0xFFFF);
+
+ PrintToStdOut("Do you want to let libCEC try to autodetect the address (y/n)?");
+ string input;
+ getline(cin, input);
+ cin.clear();
+ if (input == "y" || input == "Y")
+ {
+ cec_logical_address baseDevice = FindPhysicalAddressBaseDevice();
+ if (baseDevice == CECDEVICE_UNKNOWN)
+ return iAddress;
+
+ int8_t iPortNumber = FindPhysicalAddressPortNumber();
+ if (iPortNumber == -1)
+ return iAddress;
+
+ PrintToStdOut("Trying to detect the physical address...");
+ if (!g_parser->SetHDMIPort(baseDevice, iPortNumber))
+ PrintToStdOut("Failed. Please enter the address manually, or restart this wizard and use different settings.");
+ else
+ {
+ iAddress = g_parser->GetDevicePhysicalAddress(g_primaryAddress);
+ if (iAddress == 0 || iAddress == 0xFFFF)
+ PrintToStdOut("Failed. Please enter the address manually, or restart this wizard and use different settings.");
+ }
+ }
+
+ if (iAddress == 0 || iAddress == 0xFFFF)
+ {
+ PrintToStdOut("Please enter the physical address (0000 - FFFF), followed by <enter>.");
+ getline(cin, input);
+ cin.clear();
+
+ int iAddressTmp;
+ if (sscanf(input.c_str(), "%x", &iAddressTmp) == 1)
+ {
+ if (iAddressTmp < 0 || iAddressTmp > 0xFFFF)
+ iAddressTmp = 0xFFFF;
+ iAddress = (uint16_t)iAddressTmp;
+ }
+ }
+
+ return iAddress;
+}
+
+bool PowerOnTV(uint64_t iTimeout = 60000, unsigned iTries = 2)
+{
+ cec_power_status currentTvPower(CEC_POWER_STATUS_UNKNOWN);
+ uint64_t iNow = GetTimeMs();
+ uint64_t iTarget = iNow + iTimeout;
+ unsigned iTry(0);
+
+ while (currentTvPower != CEC_POWER_STATUS_ON && iTarget > iNow && iTry < iTries)
+ {
+ currentTvPower = g_parser->GetDevicePowerStatus(CECDEVICE_TV);
+ if (currentTvPower != CEC_POWER_STATUS_ON)
+ {
+ PrintToStdOut("Sending 'power on' command to the TV");
+ g_parser->PowerOnDevices(CECDEVICE_TV);
+ while (iTarget > iNow)
+ {
+ CLockObject lock(g_responseMutex);
+ g_responseCondtion.Wait(g_responseMutex, (uint32_t)((iTarget - iNow)/iTries));
+ if (g_lastCommand == CEC_OPCODE_REQUEST_ACTIVE_SOURCE)
+ break;
+ iNow = GetTimeMs();
+ }
+ }
+ }
+
+ currentTvPower = g_parser->GetDevicePowerStatus(CECDEVICE_TV);
+
+ if (currentTvPower != CEC_POWER_STATUS_ON)
+ PrintToStdOut("Failed to power on the TV, or the TV does not respond properly");
+
+ return currentTvPower == CEC_POWER_STATUS_ON;
+}
+
+int main (int argc, char *argv[])
+{
+ PrintToStdOut("=== USB-CEC Adapter Configuration ===\n");
+ if (!OpenConnection())
+ return 1;
+
+ if (!PowerOnTV())
+ return 1;
+
+ bool bAddressOk(false);
+ uint16_t iAddress(0xFFF);
+ while (!bAddressOk)
+ {
+ iAddress = FindPhysicalAddress();
+
+ PrintToStdOut("Physical address: %4X", iAddress);
+ PrintToStdOut("Is this correct (y/n)?");
+ string input;
+ getline(cin, input);
+ cin.clear();
+ bAddressOk = (input == "y" || input == "Y");
+ }
+
+ PrintToStdOut("=== USB-CEC Adapter Configuration Summary ===\n");
+ PrintToStdOut("Physical address: %4X", iAddress);
+
+ g_parser->StandbyDevices();
+ g_parser->Close();
+ UnloadLibCec(g_parser);
+
+ PrintToStdOut("Press enter to close this wizard.");
+ string input;
+ getline(cin, input);
+ return 0;
+}