Merge branch 'master' into release
authorLars Op den Kamp <lars@opdenkamp.eu>
Thu, 12 Jan 2012 22:41:36 +0000 (23:41 +0100)
committerLars Op den Kamp <lars@opdenkamp.eu>
Thu, 12 Jan 2012 22:41:36 +0000 (23:41 +0100)
25 files changed:
ChangeLog
configure.ac
debian/changelog
debian/rules
include/cec.h
include/cecc.h
include/cectypes.h
project/libcec.rc
project/testclient.rc
src/CecSharpTester/AssemblyInfo.cs
src/CecSharpTester/CecSharpClient.cs
src/CecSharpTester/CecSharpClient.csproj
src/LibCecSharp/AssemblyInfo.cpp
src/LibCecSharp/LibCecSharp.cpp
src/lib/AdapterCommunication.cpp
src/lib/AdapterDetection.cpp
src/lib/CECProcessor.cpp
src/lib/LibCEC.cpp
src/lib/LibCEC.h
src/lib/LibCECC.cpp
src/lib/devices/CECBusDevice.cpp
src/lib/devices/CECBusDevice.h
src/lib/implementations/CECCommandHandler.cpp
src/lib/implementations/CECCommandHandler.h
src/testclient/main.cpp

index 7bd454739b57597e412d02d4b86ef3ea8896573a..fbf5d237ae7d516a39dc2c60e6d17de634a8d1c2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,30 @@
-libcec (1.3-3) unstable; urgency=low
+libcec (1.4-1) unstable; urgency=low
+
+   * changed/added:
+     * added the vendor id for Sony
+     * always refresh the power state of a device when it hasn't been updated
+       for 30 seconds
+     * do silent builds by default
+   * interface changes:
+     * added optional callback methods to libCEC. enable them by calling
+       EnableCallbacks(ICECCallbacks *callbacks) /
+       cec_enable_callbacks(ICECCallbacks *callbacks). after this method is
+       called, the GetNext...() methods will not return any data
+     * added the same callbacks to LibCecSharp. implement CecCallbackMethods
+       and override the methods in there
+   * fixed:
+     * use the given timeout when trying to open a connection to the CEC
+       adapter
+     * resolved difference between method name in LibCECC.cpp and cecc.h.
+       credits: Doug Johnson
+     * don't transmit physical addresses while holding a lock in CCECProcessor
+     * don't hold a lock when sending an active source message.
+     * unload libCEC when the lib version is invalid
+     * "unused" warnings suppressed     
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com>  Thu, 12 Jan 2012 19:06:00 +0100
+ libcec (1.3-3) unstable; urgency=low
 
    * changed/added:
      * place in libudev include in an extern C block. fixes compilations on
index 6c8c7fda33a0c5ae35968149defae2b53c73ef48..ebb68da4a7640259c693efa7497aeefed439594f 100644 (file)
@@ -1,6 +1,8 @@
-AC_INIT([libcec], 1:3:0)
+AC_INIT([libcec], 1:4:0)
 AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
 
+AM_SILENT_RULES([yes])
+
 AC_PROG_CXX
 AC_PROG_LIBTOOL
 
@@ -30,7 +32,7 @@ libs_pre_dl=$LIBS
   AC_SUBST([LIBS_DL])
 LIBS=$libs_pre_dl
 
-CXXFLAGS="-fPIC -Wall -Wextra $CXXFLAGS"
+CXXFLAGS="-fPIC -Wall -Wextra -Wno-missing-field-initializers $CXXFLAGS"
 
 AC_SUBST(REQUIRES)
 AC_CONFIG_FILES([src/lib/libcec.pc])
index 7bd454739b57597e412d02d4b86ef3ea8896573a..fbf5d237ae7d516a39dc2c60e6d17de634a8d1c2 100644 (file)
@@ -1,4 +1,30 @@
-libcec (1.3-3) unstable; urgency=low
+libcec (1.4-1) unstable; urgency=low
+
+   * changed/added:
+     * added the vendor id for Sony
+     * always refresh the power state of a device when it hasn't been updated
+       for 30 seconds
+     * do silent builds by default
+   * interface changes:
+     * added optional callback methods to libCEC. enable them by calling
+       EnableCallbacks(ICECCallbacks *callbacks) /
+       cec_enable_callbacks(ICECCallbacks *callbacks). after this method is
+       called, the GetNext...() methods will not return any data
+     * added the same callbacks to LibCecSharp. implement CecCallbackMethods
+       and override the methods in there
+   * fixed:
+     * use the given timeout when trying to open a connection to the CEC
+       adapter
+     * resolved difference between method name in LibCECC.cpp and cecc.h.
+       credits: Doug Johnson
+     * don't transmit physical addresses while holding a lock in CCECProcessor
+     * don't hold a lock when sending an active source message.
+     * unload libCEC when the lib version is invalid
+     * "unused" warnings suppressed     
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com>  Thu, 12 Jan 2012 19:06:00 +0100
+ libcec (1.3-3) unstable; urgency=low
 
    * changed/added:
      * place in libudev include in an extern C block. fixes compilations on
index f9d1196afb5ccacac49477f7d71436cb6d6e833b..5205032cbaedd6a5078634b4261170dc96385f95 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright Pulse-Eight 2011
 
 # Uncomment this to turn on verbose mode.
-export DH_VERBOSE=1
+#export DH_VERBOSE=1
 
 DEB_BUILD_GNU_TYPE := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
 ifeq ($(DEB_BUILD_GNU_TYPE), $(DEB_HOST_GNU_TYPE))
index 61e4bb315b50668bf1f68bd19b2aae18c440533c..9e09902fe01aa1e676dcec72f1687846f7c395eb 100644 (file)
@@ -59,7 +59,15 @@ namespace CEC
     virtual void Close(void) = 0;
 
     /*!
-     * @brief Try to find all connected CEC adapters. Only implemented on Linux at the moment.
+     * @brief Set and enable the callback methods. If this method is not called, the GetNext...() methods will have to be used.
+     * @param cbParam Parameter to pass to callback methods.
+     * @param callbacks The callbacks to set.
+     * @return True when enabled, false otherwise.
+     */
+    virtual bool EnableCallbacks(void *cbParam, ICECCallbacks *callbacks) = 0;
+
+    /*!
+     * @brief Try to find all connected CEC adapters. Only implemented on Linux and Windows at the moment.
      * @param deviceList The vector to store device descriptors in.
      * @param iBufSize The size of the deviceList buffer.
      * @param strDevicePath Optional device path. Only adds device descriptors that match the given device path.
@@ -352,7 +360,6 @@ namespace CEC
     virtual const char *ToString(const cec_system_audio_status mode) = 0;
     virtual const char *ToString(const cec_audio_status status) = 0;
     virtual const char *ToString(const cec_vendor_id vendor) = 0;
-
   };
 };
 
index ddb141b714f02f577de6771786f984cbeeab11fb..8c9bc932b82907edcf8936c6f45d31bde95bff6d 100644 (file)
@@ -59,6 +59,12 @@ extern DECLSPEC int cec_open(const char *strPort, uint32_t iTimeout);
 
 extern DECLSPEC void cec_close(void);
 
+#ifdef __cplusplus
+extern DECLSPEC int cec_enable_callbacks(void *cbParam, CEC::ICECCallbacks *callbacks);
+#else
+extern DECLSPEC int cec_enable_callbacks(void *cbParam, ICECCallbacks *callbacks);
+#endif
+
 #ifdef __cplusplus
 extern DECLSPEC int8_t cec_find_adapters(CEC::cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath);
 #else
index f2b2f02b3c9c49fc4060474833ef792fdc49a7f7..ba21200fece893f93d0c13cde4be1f56d6ca5828 100644 (file)
 #include <stdint.h>
 #include <string.h>
 
+#if defined(_WIN32) || defined(_WIN64)
+#define CEC_CDECL    __cdecl
+#else
+#define CEC_CDECL
+#endif
+
 #if !defined(DECLSPEC)
 #if defined(_WIN32) || defined(_WIN64)
 #include <windows.h>
@@ -64,6 +70,7 @@ namespace CEC {
 #define MSGESC                       0xFD
 #define ESCOFFSET                    3
 #define CEC_BUTTON_TIMEOUT           500
+#define CEC_POWER_STATE_REFRESH_TIME 30000
 
 #define CEC_DEFAULT_TRANSMIT_TIMEOUT 1000
 #define CEC_DEFAULT_TRANSMIT_WAIT    2000
@@ -71,7 +78,7 @@ namespace CEC {
 
 #define CEC_MIN_LIB_VERSION          1
 #define CEC_LIB_VERSION_MAJOR        1
-#define CEC_LIB_VERSION_MINOR        3
+#define CEC_LIB_VERSION_MINOR        4
 
 typedef enum cec_abort_reason
 {
@@ -599,13 +606,14 @@ typedef enum cec_bus_device_status
 
 typedef enum cec_vendor_id
 {
-  CEC_VENDOR_SAMSUNG   = 0x00F0,
-  CEC_VENDOR_LG        = 0xE091,
-  CEC_VENDOR_PANASONIC = 0x8045,
-  CEC_VENDOR_PIONEER   = 0xE036,
-  CEC_VENDOR_ONKYO     = 0x09B0,
-  CEC_VENDOR_YAMAHA    = 0xA0DE,
-  CEC_VENDOR_PHILIPS   = 0x903E,
+  CEC_VENDOR_SAMSUNG   = 0x0000F0,
+  CEC_VENDOR_LG        = 0x00E091,
+  CEC_VENDOR_PANASONIC = 0x008045,
+  CEC_VENDOR_PIONEER   = 0x00E036,
+  CEC_VENDOR_ONKYO     = 0x0009B0,
+  CEC_VENDOR_YAMAHA    = 0x00A0DE,
+  CEC_VENDOR_PHILIPS   = 0x00903E,
+  CEC_VENDOR_SONY      = 0x080046,
   CEC_VENDOR_UNKNOWN   = 0
 } cec_vendor_id;
 
@@ -864,6 +872,44 @@ typedef struct cec_logical_addresses
 #endif
 } cec_logical_addresses;
 
+
+typedef int (CEC_CDECL* CBCecLogMessageType)(void *param, const CEC::cec_log_message &);
+typedef int (CEC_CDECL* CBCecKeyPressType)(void *param, const cec_keypress &key);
+typedef int (CEC_CDECL* CBCecCommandType)(void *param, const cec_command &command);
+
+typedef struct ICECCallbacks
+{
+  /*!
+   * @brief Transfer a log message from libCEC to the client.
+   * @param message The message to transfer.
+   * @return 1 when ok, 0 otherwise.
+   */
+  CBCecLogMessageType CBCecLogMessage;
+
+  /*!
+   * @brief Transfer a keypress from libCEC to the client.
+   * @param key The keypress to transfer.
+   * @return 1 when ok, 0 otherwise.
+   */
+  CBCecKeyPressType CBCecKeyPress;
+
+  /*!
+   * @brief Transfer a CEC command from libCEC to the client.
+   * @param command The command to transfer.
+   * @return 1 when ok, 0 otherwise.
+   */
+  CBCecCommandType CBCecCommand;
+} ICECCallbacks;
+
+#ifdef UNUSED
+#elif defined(__GNUC__)
+#define UNUSED(x) UNUSED_ ## x __attribute__((unused))
+#elif defined(__LCLINT__)
+#define UNUSED(x) /*@unused@*/ x
+#else
+#define UNUSED(x) x
+#endif
+
 #ifdef __cplusplus
 };
 };
index e18b9a311b2c23052b1cef4c0301dff396674f3b..2b1e541067631eedcfde7de56b18f34d9b01706e 100644 (file)
Binary files a/project/libcec.rc and b/project/libcec.rc differ
index 339801b3d2bb0e601199803863fe9aa24254f54f..f80c470ef13aec4dc8614fc258541c45b96d3b3a 100644 (file)
Binary files a/project/testclient.rc and b/project/testclient.rc differ
index 73ffc394222fc35a3d70a002c96228bd5639d8a0..d742067945bb586f2971f1563fb4eb4199579f14 100644 (file)
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
 [assembly: AssemblyConfiguration("")]
 [assembly: AssemblyCompany("Pulse-Eight Ltd.")]
 [assembly: AssemblyProduct("CecSharpClient")]
-[assembly: AssemblyCopyright("Copyright (c) Pulse-Eight Ltd. 2012)]
+[assembly: AssemblyCopyright("Copyright (c) Pulse-Eight Ltd. 2012)")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]
 
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
 // You can specify all the values or you can default the Build and Revision Numbers 
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.3.2.0")]
-[assembly: AssemblyFileVersion("1.3.2.0")]
+[assembly: AssemblyVersion("1.4.0.0")]
+[assembly: AssemblyFileVersion("1.4.0.0")]
index 3d8ec32c4588ae593750f7a463f21944a072f67e..342d7c094e1b74eeffedce288953221007f960a0 100644 (file)
@@ -37,7 +37,7 @@ using System.Text;
 
 namespace CecSharpClient
 {
-  class CecSharpClient
+  class CecSharpClient : CecCallbackMethods
   {
     public CecSharpClient()
     {
@@ -50,6 +50,47 @@ namespace CecSharpClient
       Console.WriteLine("CEC Parser created - libcec version " + Lib.GetLibVersionMajor() + "." + Lib.GetLibVersionMinor());
     }
 
+    public override int ReceiveCommand(CecCommand command)
+    {
+      return 1;
+    }
+
+    public override int ReceiveKeypress(CecKeypress key)
+    {
+      return 1;
+    }
+
+    public override int ReceiveLogMessage(CecLogMessage message)
+    {
+      if (((int)message.Level & LogLevel) == (int)message.Level)
+      {
+        string strLevel = "";
+        switch (message.Level)
+        {
+          case CecLogLevel.Error:
+            strLevel = "ERROR:   ";
+            break;
+          case CecLogLevel.Warning:
+            strLevel = "WARNING: ";
+            break;
+          case CecLogLevel.Notice:
+            strLevel = "NOTICE:  ";
+            break;
+          case CecLogLevel.Traffic:
+            strLevel = "TRAFFIC: ";
+            break;
+          case CecLogLevel.Debug:
+            strLevel = "DEBUG:   ";
+            break;
+          default:
+            break;
+        }
+        string strLog = string.Format("{0} {1,16} {2}", strLevel, message.Time, message.Message);
+        Console.WriteLine(strLog);
+      }
+      return 1;
+    }
+
     void FlushLog()
     {
       CecLogMessage message = Lib.GetNextLogMessage();
@@ -154,17 +195,15 @@ namespace CecSharpClient
 
     public void MainLoop()
     {
-      Lib.PowerOnDevices(CecLogicalAddress.Tv);
-      FlushLog();
+      Lib.EnableCallbacks(this);
 
+      Lib.PowerOnDevices(CecLogicalAddress.Tv);
       Lib.SetActiveSource(CecDeviceType.PlaybackDevice);
-      FlushLog();
 
       bool bContinue = true;
       string command;
       while (bContinue)
       {
-        FlushLog();
         Console.WriteLine("waiting for input");
 
         command = Console.ReadLine();
@@ -283,11 +322,9 @@ namespace CecSharpClient
         {
           Console.WriteLine("closing the connection");
           Lib.Close();
-          FlushLog();
 
           Console.WriteLine("opening a new connection");
           Connect(10000);
-          FlushLog();
 
           Console.WriteLine("setting active source");
           Lib.SetActiveSource(CecDeviceType.PlaybackDevice);
@@ -346,7 +383,6 @@ namespace CecSharpClient
       {
         Console.WriteLine("Could not open a connection to the CEC adapter");
       }
-      p.FlushLog();
     }
 
     private int         LogLevel;
index eff98ef6b9af9d21b31e1e1b937d17c35a2330b5..9a7e65dda6b673213d84499b074ac8599425fe27 100644 (file)
@@ -34,7 +34,7 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="LibCecSharp, Version=1.0.4334.36379, Culture=neutral, processorArchitecture=x86">
+    <Reference Include="LibCecSharp, Version=1.3.2.0, Culture=neutral, processorArchitecture=x86">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\..\LibCecSharp.dll</HintPath>
     </Reference>
index 0624b82a9c91662984d5494958b43fd37f3df380..c5dd8e2e0025cb228a23e20026bb144bae9b111f 100644 (file)
@@ -31,7 +31,7 @@ using namespace System::Security::Permissions;
 // You can specify all the value or you can default the Revision and Build Numbers
 // by using the '*' as shown below:
 
-[assembly:AssemblyVersionAttribute("1.3.2.0")];
+[assembly:AssemblyVersionAttribute("1.4.0.0")];
 
 [assembly:ComVisible(false)];
 
index 088c9ef51c70637df6e2e7de592b4b08a070a81e..3aa1253512034786373e2ad432bfeff429ae623a 100644 (file)
@@ -38,6 +38,7 @@
 #using <System.dll>
 
 using namespace System;
+using namespace System::Runtime::InteropServices;
 using namespace CEC;
 using namespace msclr::interop;
 
@@ -494,25 +495,102 @@ public:
   property int64_t     Time;
 };
 
+public ref class CecCallbackMethods
+{
+public:
+  virtual int ReceiveLogMessage(CecLogMessage ^ message)
+  {
+    return 0;
+  }
+
+  virtual int ReceiveKeypress(CecKeypress ^ key)
+  {
+    return 0;
+  }
+
+  virtual int ReceiveCommand(CecCommand ^ command)
+  {
+    return 0;
+  }
+};
+
+#pragma unmanaged
+// unmanaged callback methods
+typedef int (__stdcall *LOGCB)    (const cec_log_message &message);
+typedef int (__stdcall *KEYCB)    (const cec_keypress &key);
+typedef int (__stdcall *COMMANDCB)(const cec_command &command);
+
+static LOGCB         g_logCB;
+static KEYCB         g_keyCB;
+static COMMANDCB     g_commandCB;
+static ICECCallbacks g_cecCallbacks;
+
+int CecLogMessageCB(void *cbParam, const cec_log_message &message)
+{
+  if (g_logCB)
+    return g_logCB(message);
+  return 0;
+}
+
+int CecKeyPressCB(void *cbParam, const cec_keypress &key)
+{
+  if (g_keyCB)
+    return g_keyCB(key);
+  return 0;
+}
+
+int CecCommandCB(void *cbParam, const cec_command &command)
+{
+  if (g_commandCB)
+    return g_commandCB(command);
+  return 0;
+}
+
+#pragma managed
+// delegates for the unmanaged callback methods
+public delegate int CecLogMessageManagedDelegate(const cec_log_message &);
+public delegate int CecKeyPressManagedDelegate(const cec_keypress &);
+public delegate int CecCommandManagedDelegate(const cec_command &);
+
 public ref class LibCecSharp
 {
 public:
-   LibCecSharp(String ^ strDeviceName, CecDeviceTypeList ^ deviceTypes)
-   {
-     marshal_context ^ context = gcnew marshal_context();
+  LibCecSharp(String ^ strDeviceName, CecDeviceTypeList ^ deviceTypes)
+  {
+    marshal_context ^ context = gcnew marshal_context();
+    m_bHasCallbacks = false;
+    const char* strDeviceNameC = context->marshal_as<const char*>(strDeviceName);
 
-     const char* strDeviceNameC = context->marshal_as<const char*>(strDeviceName);
+    cec_device_type_list types;
+    for (unsigned int iPtr = 0; iPtr < 5; iPtr++)
+      types.types[iPtr] = (cec_device_type)deviceTypes->Types[iPtr];
+    m_libCec = (ICECAdapter *) CECInit(strDeviceNameC, types);
+
+    // create the delegate method for the log message callback
+    m_logMessageDelegate           = gcnew CecLogMessageManagedDelegate(this, &LibCecSharp::CecLogMessageManaged);
+    m_logMessageGCHandle           = GCHandle::Alloc(m_logMessageDelegate);
+    g_logCB                        = static_cast<LOGCB>(Marshal::GetFunctionPointerForDelegate(m_logMessageDelegate).ToPointer());
+    g_cecCallbacks.CBCecLogMessage = CecLogMessageCB;
+
+    // create the delegate method for the keypress callback
+    m_keypressDelegate           = gcnew CecKeyPressManagedDelegate(this, &LibCecSharp::CecKeyPressManaged);
+    m_keypressGCHandle           = GCHandle::Alloc(m_keypressDelegate);
+    g_keyCB                      = static_cast<KEYCB>(Marshal::GetFunctionPointerForDelegate(m_keypressDelegate).ToPointer());
+    g_cecCallbacks.CBCecKeyPress = CecKeyPressCB;
+
+    // create the delegate method for the command callback
+    m_commandDelegate           = gcnew CecCommandManagedDelegate(this, &LibCecSharp::CecCommandManaged);
+    m_commandGCHandle           = GCHandle::Alloc(m_commandDelegate);
+    g_commandCB                 = static_cast<COMMANDCB>(Marshal::GetFunctionPointerForDelegate(m_commandDelegate).ToPointer());
+    g_cecCallbacks.CBCecCommand = CecCommandCB;
 
-     cec_device_type_list types;
-     for (unsigned int iPtr = 0; iPtr < 5; iPtr++)
-       types.types[iPtr] = (cec_device_type)deviceTypes->Types[iPtr];
-     m_libCec = (ICECAdapter *) CECInit(strDeviceNameC, types);
-     delete context;
-   }
+    delete context;
+  }
    
    ~LibCecSharp(void)
    {
      CECDestroy(m_libCec);
+     DestroyDelegates();
      m_libCec = NULL;
    }
 
@@ -520,6 +598,7 @@ protected:
    !LibCecSharp(void)
    {
      CECDestroy(m_libCec);
+     DestroyDelegates();
      m_libCec = NULL;
    }
 
@@ -556,6 +635,18 @@ public:
     m_libCec->Close();
   }
 
+  bool EnableCallbacks(CecCallbackMethods ^ callbacks)
+  {
+    if (m_libCec && !m_bHasCallbacks)
+    {
+      m_bHasCallbacks = true;
+      m_callbacks = callbacks;
+      return m_libCec->EnableCallbacks(NULL, &g_cecCallbacks);
+    }
+
+    return false;
+  }
+
   bool PingAdapter(void)
   {
     return m_libCec->PingAdapter();
@@ -858,5 +949,56 @@ public:
   }
 
 private:
-   ICECAdapter *m_libCec;
+  void DestroyDelegates()
+  {
+    m_logMessageGCHandle.Free();
+    m_keypressGCHandle.Free();
+    m_commandGCHandle.Free();
+  }
+
+  // managed callback methods
+  int CecLogMessageManaged(const cec_log_message &message)
+  {
+    int iReturn(0);
+    if (m_bHasCallbacks)
+      iReturn = m_callbacks->ReceiveLogMessage(gcnew CecLogMessage(gcnew String(message.message), (CecLogLevel)message.level, message.time));
+    return iReturn;
+  }
+
+  int CecKeyPressManaged(const cec_keypress &key)
+  {
+    int iReturn(0);
+    if (m_bHasCallbacks)
+      iReturn = m_callbacks->ReceiveKeypress(gcnew CecKeypress(key.keycode, key.duration));
+    return iReturn;
+  }
+
+  int CecCommandManaged(const cec_command &command)
+  {
+    int iReturn(0);
+    if (m_bHasCallbacks)
+    {
+      CecCommand ^ newCommand = gcnew CecCommand((CecLogicalAddress)command.initiator, (CecLogicalAddress)command.destination, command.ack == 1 ? true : false, command.eom == 1 ? true : false, (CecOpcode)command.opcode, command.transmit_timeout);
+      for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
+        newCommand->Parameters->PushBack(command.parameters[iPtr]);
+      iReturn = m_callbacks->ReceiveCommand(newCommand);
+    }
+    return iReturn;
+  }
+
+  ICECAdapter *        m_libCec;
+  CecCallbackMethods ^ m_callbacks;
+  bool                 m_bHasCallbacks;
+
+  CecLogMessageManagedDelegate ^ m_logMessageDelegate;
+  static GCHandle                m_logMessageGCHandle;
+  LOGCB                          m_logMessageCallback;
+
+  CecKeyPressManagedDelegate ^   m_keypressDelegate;
+  static GCHandle                m_keypressGCHandle;
+  KEYCB                          m_keypressCallback;
+
+  CecCommandManagedDelegate ^    m_commandDelegate;
+  static GCHandle                m_commandGCHandle;
+  COMMANDCB                      m_commandCallback;
 };
index 54077d6ed776b4b6ef72f0dcc0d76cffd948ab45..8d351f058a49164409bd11380211934aa1d650a7 100644 (file)
@@ -265,7 +265,11 @@ CAdapterCommunication::~CAdapterCommunication(void)
 
 bool CAdapterCommunication::Open(const char *strPort, uint16_t iBaudRate /* = 38400 */, uint32_t iTimeoutMs /* = 10000 */)
 {
+  uint64_t iNow = GetTimeMs();
+  uint64_t iTimeout = iNow + iTimeoutMs;
+
   CLockObject lock(&m_mutex);
+
   if (!m_port)
   {
     m_processor->AddLog(CEC_LOG_ERROR, "port is NULL");
@@ -275,12 +279,23 @@ bool CAdapterCommunication::Open(const char *strPort, uint16_t iBaudRate /* = 38
   if (IsOpen())
   {
     m_processor->AddLog(CEC_LOG_ERROR, "port is already open");
+    return true;
   }
 
-  if (!m_port->Open(strPort, iBaudRate))
+  CStdString strError;
+  bool bConnected(false);
+  while (!bConnected && iNow < iTimeout)
+  {
+    if ((bConnected = m_port->Open(strPort, iBaudRate)) == false)
+    {
+      strError.Format("error opening serial port '%s': %s", strPort, m_port->GetError().c_str());
+      Sleep(250);
+      iNow = GetTimeMs();
+    }
+  }
+
+  if (!bConnected)
   {
-    CStdString strError;
-    strError.Format("error opening serial port '%s': %s", strPort, m_port->GetError().c_str());
     m_processor->AddLog(CEC_LOG_ERROR, strError);
     return false;
   }
index de1da7c1f3fb2c435d1fbc6f255ec0a9b86eb192..f06809975abc49300558e06780b4445633f6dd6c 100644 (file)
@@ -324,5 +324,7 @@ uint8_t CAdapterDetection::FindAdapters(cec_adapter *deviceList, uint8_t iBufSiz
   }
 #endif
 
+  iBufSize = 0; /* silence "unused" warning on linux/osx */
+
   return iFound;
 }
index 5071ec6ee3f8b87818b8f5733694fedf030126d2..798c38a422cda0c8a855cac235a898d849961cc3 100644 (file)
@@ -527,12 +527,16 @@ bool CCECProcessor::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort,
     else if (iPhysicalAddress % 0x10 == 0)
       iPhysicalAddress += iPort;
 
-    SetPhysicalAddress(iPhysicalAddress);
     bReturn = true;
   }
 
   if (!bReturn)
     m_controller->AddLog(CEC_LOG_ERROR, "failed to set the physical address");
+  else
+  {
+    lock.Leave();
+    SetPhysicalAddress(iPhysicalAddress);
+  }
 
   return bReturn;
 }
@@ -601,23 +605,38 @@ bool CCECProcessor::SetMenuState(cec_menu_state state, bool bSendUpdate /* = tru
 
 bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress, bool bSendUpdate /* = true */)
 {
-  bool bWasActiveSource(false);
-  CLockObject lock(&m_mutex);
-  if (!m_logicalAddresses.IsEmpty())
+  bool bSendActiveView(false);
+  bool bReturn(false);
+  cec_logical_addresses sendUpdatesTo;
+
   {
-    for (uint8_t iPtr = 0; iPtr < 15; iPtr++)
-      if (m_logicalAddresses[iPtr])
-      {
-        bWasActiveSource |= m_busDevices[iPtr]->IsActiveSource();
-        m_busDevices[iPtr]->SetInactiveSource();
-        m_busDevices[iPtr]->SetPhysicalAddress(iPhysicalAddress);
-        if (bSendUpdate)
-          m_busDevices[iPtr]->TransmitPhysicalAddress();
-      }
+    CLockObject lock(&m_mutex);
+    if (!m_logicalAddresses.IsEmpty())
+    {
+      bool bWasActiveSource(false);
+      for (uint8_t iPtr = 0; iPtr < 15; iPtr++)
+        if (m_logicalAddresses[iPtr])
+        {
+          bWasActiveSource |= m_busDevices[iPtr]->IsActiveSource();
+          m_busDevices[iPtr]->SetInactiveSource();
+          m_busDevices[iPtr]->SetPhysicalAddress(iPhysicalAddress);
+          if (bSendUpdate)
+            sendUpdatesTo.Set((cec_logical_address)iPtr);
+        }
 
-    return bWasActiveSource && bSendUpdate ? SetActiveView() : true;
+      bSendActiveView = bWasActiveSource && bSendUpdate;
+      bReturn = true;
+    }
   }
-  return false;
+
+  for (uint8_t iPtr = 0; iPtr < 15; iPtr++)
+    if (sendUpdatesTo[iPtr])
+      m_busDevices[iPtr]->TransmitPhysicalAddress();
+
+  if (bSendActiveView)
+    SetActiveView();
+
+  return bReturn;
 }
 
 bool CCECProcessor::SwitchMonitoring(bool bEnable)
@@ -1397,7 +1416,7 @@ const char *CCECProcessor::ToString(const cec_system_audio_status mode)
   }
 }
 
-const char *CCECProcessor::ToString(const cec_audio_status status)
+const char *CCECProcessor::ToString(const cec_audio_status UNUSED(status))
 {
   // TODO this is a mask
   return "TODO";
@@ -1421,6 +1440,8 @@ const char *CCECProcessor::ToString(const cec_vendor_id vendor)
     return "Yamaha";
   case CEC_VENDOR_PHILIPS:
     return "Philips";
+  case CEC_VENDOR_SONY:
+    return "Sony";
   default:
     return "Unknown";
   }
index 1366ff6cbcec92b6567ab0648e8a32cdeac3f87a..52dfa515776a7beada82a77e5c831b7f24209945 100644 (file)
@@ -45,7 +45,9 @@ using namespace CEC;
 CLibCEC::CLibCEC(const char *strDeviceName, cec_device_type_list types) :
     m_iStartTime(GetTimeMs()),
     m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN),
-    m_buttontime(0)
+    m_buttontime(0),
+    m_callbacks(NULL),
+    m_cbParam(NULL)
 {
   m_cec = new CCECProcessor(this, strDeviceName, types);
 }
@@ -53,7 +55,9 @@ CLibCEC::CLibCEC(const char *strDeviceName, cec_device_type_list types) :
 CLibCEC::CLibCEC(const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, uint16_t iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS */) :
     m_iStartTime(GetTimeMs()),
     m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN),
-    m_buttontime(0)
+    m_buttontime(0),
+    m_callbacks(NULL),
+    m_cbParam(NULL)
 {
   m_cec = new CCECProcessor(this, strDeviceName, iLogicalAddress, iPhysicalAddress);
 }
@@ -87,6 +91,17 @@ void CLibCEC::Close(void)
     m_cec->StopThread();
 }
 
+bool CLibCEC::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
+{
+  CLockObject lock(&m_mutex);
+  if (m_cec)
+  {
+    m_cbParam   = cbParam;
+    m_callbacks = callbacks;
+  }
+  return false;
+}
+
 int8_t CLibCEC::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */)
 {
   CStdString strDebug;
@@ -328,32 +343,46 @@ cec_osd_name CLibCEC::GetDeviceOSDName(cec_logical_address iAddress)
 
 void CLibCEC::AddLog(cec_log_level level, const string &strMessage)
 {
+  CLockObject lock(&m_mutex);
   if (m_cec)
   {
     cec_log_message message;
     message.level = level;
     message.time = GetTimeMs() - m_iStartTime;
     snprintf(message.message, sizeof(message.message), "%s", strMessage.c_str());
-    m_logBuffer.Push(message);
+
+    if (m_callbacks)
+      m_callbacks->CBCecLogMessage(m_cbParam, message);
+    else
+      m_logBuffer.Push(message);
   }
 }
 
 void CLibCEC::AddKey(cec_keypress &key)
 {
-  m_keyBuffer.Push(key);
+  CLockObject lock(&m_mutex);
+  if (m_callbacks)
+    m_callbacks->CBCecKeyPress(m_cbParam, key);
+  else
+    m_keyBuffer.Push(key);
   m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
   m_buttontime = 0;
 }
 
 void CLibCEC::AddKey(void)
 {
+  CLockObject lock(&m_mutex);
   if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN)
   {
     cec_keypress key;
 
     key.duration = (unsigned int) (GetTimeMs() - m_buttontime);
     key.keycode = m_iCurrentButton;
-    m_keyBuffer.Push(key);
+
+    if (m_callbacks)
+      m_callbacks->CBCecKeyPress(m_cbParam, key);
+    else
+      m_keyBuffer.Push(key);
     m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
   }
   m_buttontime = 0;
@@ -361,7 +390,12 @@ void CLibCEC::AddKey(void)
 
 void CLibCEC::AddCommand(const cec_command &command)
 {
-  if (m_commandBuffer.Push(command))
+  CLockObject lock(&m_mutex);
+  if (m_callbacks)
+  {
+    m_callbacks->CBCecCommand(m_cbParam, command);
+  }
+  else if (m_commandBuffer.Push(command))
   {
     CStdString strDebug;
     strDebug.Format("stored command '%2x' in the command buffer. buffer size = %d", command.opcode, m_commandBuffer.Size());
index 9c3e8f1002c235bd73f5ea150cb47346f794ab86..ef1ded29f5c5fdee7272f4fd44f5f8b1e33513b9 100644 (file)
@@ -53,6 +53,7 @@ namespace CEC
 
       virtual bool Open(const char *strPort, uint32_t iTimeout = 10000);
       virtual void Close(void);
+      virtual bool EnableCallbacks(void *cbParam, ICECCallbacks *callbacks);
       virtual int8_t FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL);
       virtual bool PingAdapter(void);
       virtual bool StartBootloader(void);
@@ -126,5 +127,8 @@ namespace CEC
       CecBuffer<cec_log_message> m_logBuffer;
       CecBuffer<cec_keypress>    m_keyBuffer;
       CecBuffer<cec_command>     m_commandBuffer;
+      ICECCallbacks             *m_callbacks;
+      void                      *m_cbParam;
+      CMutex                     m_mutex;
   };
 };
index cbf7b285ec8a8223eb9a2f25fec26ad21e7d2cb7..91ab395eca5fa61e30657bbede26abb8c4d2757e 100644 (file)
@@ -74,6 +74,13 @@ void cec_close(void)
     cec_parser->Close();
 }
 
+int cec_enable_callbacks(void *cbParam, ICECCallbacks *callbacks)
+{
+  if (cec_parser)
+    return cec_parser->EnableCallbacks(cbParam, callbacks) ? 1 : 0;
+  return -1;
+}
+
 int8_t cec_find_adapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */)
 {
   if (cec_parser)
@@ -95,7 +102,7 @@ int cec_start_bootloader(void)
   return -1;
 }
 
-int8_t cec_get_min_version(void)
+int8_t cec_get_min_lib_version(void)
 {
   if (cec_parser)
     return cec_parser->GetMinLibVersion();
index 9e9145c0e76af2f4b2abcd70c60182f189166722..d6e447f96303f44cf91735807601704451a01df0 100644 (file)
@@ -54,6 +54,7 @@ CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogi
   m_menuState(CEC_MENU_STATE_ACTIVATED),
   m_bActiveSource(false),
   m_iLastActive(0),
+  m_iLastPowerStateUpdate(0),
   m_cecVersion(CEC_VERSION_UNKNOWN),
   m_deviceStatus(CEC_DEVICE_STATUS_UNKNOWN),
   m_handlerMutex(false)
@@ -314,7 +315,9 @@ cec_power_status CCECBusDevice::GetPowerStatus(bool bUpdate /* = false */)
     CLockObject lock(&m_mutex);
     bRequestUpdate = (GetStatus() == CEC_DEVICE_STATUS_PRESENT &&
         (bUpdate || m_powerStatus == CEC_POWER_STATUS_UNKNOWN ||
-            m_powerStatus == CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON));
+            m_powerStatus == CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON ||
+            m_powerStatus == CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY ||
+            GetTimeMs() - m_iLastPowerStateUpdate >= CEC_POWER_STATE_REFRESH_TIME));
   }
 
   if (bRequestUpdate)
@@ -619,6 +622,7 @@ void CCECBusDevice::SetPowerStatus(const cec_power_status powerStatus)
   CLockObject lock(&m_mutex);
   if (m_powerStatus != powerStatus)
   {
+    m_iLastPowerStateUpdate = GetTimeMs();
     CStdString strLog;
     strLog.Format(">> %s (%X): power status changed from '%s' to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_powerStatus), ToString(powerStatus));
     m_processor->AddLog(CEC_LOG_DEBUG, strLog);
index 6157e0a7448adf81a235d9631fc28b37f5e8718d..2bef3a7657d5f61afb81c55f76d02e1950edfc9d 100644 (file)
@@ -132,6 +132,7 @@ namespace CEC
     cec_menu_state        m_menuState;
     bool                  m_bActiveSource;
     uint64_t              m_iLastActive;
+    uint64_t              m_iLastPowerStateUpdate;
     cec_version           m_cecVersion;
     cec_bus_device_status m_deviceStatus;
     std::set<cec_opcode>  m_unsupportedFeatures;
index 40b4f07ecda8755a9065b49c18a3c87327c9bfaa..bed4ff9fbb33eac6d48be69a8b46673016fd1b25 100644 (file)
@@ -639,6 +639,11 @@ bool CCECCommandHandler::HandleUserControlRelease(const cec_command &command)
   return true;
 }
 
+bool CCECCommandHandler::HandleVendorCommand(const cec_command & UNUSED(command))
+{
+  return true;
+}
+
 void CCECCommandHandler::UnhandledCommand(const cec_command &command)
 {
   CStdString strLog;
index 0e3602d9871a5cecaa929cb21110cacdc472ec9f..71baddeffcf251d4cdc00f663be3110a537a162d 100644 (file)
@@ -122,7 +122,7 @@ namespace CEC
     virtual bool HandleTextViewOn(const cec_command &command);
     virtual bool HandleUserControlPressed(const cec_command &command);
     virtual bool HandleUserControlRelease(const cec_command &command);
-    virtual bool HandleVendorCommand(const cec_command &command) { return true; }
+    virtual bool HandleVendorCommand(const cec_command &command);
     virtual void UnhandledCommand(const cec_command &command);
 
     virtual unsigned int GetMyDevices(std::vector<CCECBusDevice *> &devices) const;
index 9a0bf3a6130c9c7466f72945f96e2c0c010fe9fa..256a484e47cb91e8da42a21dca9d04619e025cd5 100644 (file)
@@ -57,7 +57,19 @@ uint8_t              g_iHDMIPort(CEC_DEFAULT_HDMI_PORT);
 cec_logical_address  g_iBaseDevice((cec_logical_address)CEC_DEFAULT_BASE_DEVICE);
 cec_device_type_list g_typeList;
 bool                 g_bSingleCommand(false);
+CMutex               g_outputMutex;
+ICECCallbacks        g_callbacks;
 
+inline void PrintToStdOut(const char *strOut)
+{
+  CLockObject lock(&g_outputMutex);
+  cout << strOut << endl;
+}
+
+inline void PrintToStdOut(const CStdString &strOut)
+{
+  PrintToStdOut(strOut.c_str());
+}
 
 inline bool HexStrToInt(const std::string& data, uint8_t& value)
 {
@@ -111,48 +123,64 @@ bool GetWord(string& data, string& word)
   return true;
 }
 
-void FlushLog(ICECAdapter *cecParser)
+int CecLogMessage(void *UNUSED(cbParam), const cec_log_message &message)
 {
-  cec_log_message message;
-  while (cecParser && cecParser->GetNextLogMessage(&message))
+  if ((message.level & g_cecLogLevel) == message.level)
   {
-    if ((message.level & g_cecLogLevel) == message.level)
+    CStdString strLevel;
+    switch (message.level)
     {
-      CStdString strLevel;
-      switch (message.level)
-      {
-      case CEC_LOG_ERROR:
-        strLevel = "ERROR:   ";
-        break;
-      case CEC_LOG_WARNING:
-        strLevel = "WARNING: ";
-        break;
-      case CEC_LOG_NOTICE:
-        strLevel = "NOTICE:  ";
-        break;
-      case CEC_LOG_TRAFFIC:
-        strLevel = "TRAFFIC: ";
-        break;
-      case CEC_LOG_DEBUG:
-        strLevel = "DEBUG:   ";
-        break;
-      default:
-        break;
-      }
+    case CEC_LOG_ERROR:
+      strLevel = "ERROR:   ";
+      break;
+    case CEC_LOG_WARNING:
+      strLevel = "WARNING: ";
+      break;
+    case CEC_LOG_NOTICE:
+      strLevel = "NOTICE:  ";
+      break;
+    case CEC_LOG_TRAFFIC:
+      strLevel = "TRAFFIC: ";
+      break;
+    case CEC_LOG_DEBUG:
+      strLevel = "DEBUG:   ";
+      break;
+    default:
+      break;
+    }
 
-      CStdString strFullLog;
-      strFullLog.Format("%s[%16lld]\t%s", strLevel.c_str(), message.time, message.message);
-      cout << strFullLog.c_str() << endl;
+    CStdString strFullLog;
+    strFullLog.Format("%s[%16lld]\t%s", strLevel.c_str(), message.time, message.message);
+    PrintToStdOut(strFullLog);
 
-      if (g_logOutput.is_open())
-      {
-        if (g_bShortLog)
-          g_logOutput << message.message << endl;
-        else
-          g_logOutput << strFullLog.c_str() << endl;
-      }
+    if (g_logOutput.is_open())
+    {
+      if (g_bShortLog)
+        g_logOutput << message.message << endl;
+      else
+        g_logOutput << strFullLog.c_str() << endl;
     }
   }
+
+  return 0;
+}
+
+int CecKeyPress(void *UNUSED(cbParam), const cec_keypress &UNUSED(key))
+{
+  return 0;
+}
+
+int CecCommand(void *UNUSED(cbParam), const cec_command &UNUSED(command))
+{
+  return 0;
+}
+
+void EnableCallbacks(ICECAdapter *adapter)
+{
+  g_callbacks.CBCecLogMessage = &CecLogMessage;
+  g_callbacks.CBCecKeyPress   = &CecKeyPress;
+  g_callbacks.CBCecCommand    = &CecCommand;
+  adapter->EnableCallbacks(NULL, &g_callbacks);
 }
 
 void ListDevices(ICECAdapter *parser)
@@ -161,24 +189,25 @@ void ListDevices(ICECAdapter *parser)
   uint8_t iDevicesFound = parser->FindAdapters(devices, 10, NULL);
   if (iDevicesFound <= 0)
   {
-    cout << "Found devices: NONE" << endl;
+    PrintToStdOut("Found devices: NONE");
   }
   else
   {
     CStdString strLog;
     strLog.Format("Found devices: %d", iDevicesFound);
-    cout << strLog.c_str() << endl;
+    PrintToStdOut(strLog);
     for (unsigned int iDevicePtr = 0; iDevicePtr < iDevicesFound; iDevicePtr++)
     {
       CStdString strDevice;
       strDevice.Format("device:        %d\npath:          %s\ncom port:      %s", iDevicePtr + 1, devices[iDevicePtr].path, devices[iDevicePtr].comm);
-      cout << endl << strDevice.c_str() << endl;
+      PrintToStdOut(strDevice);
     }
   }
 }
 
 void ShowHelpCommandLine(const char* strExec)
 {
+  CLockObject lock(&g_outputMutex);
   cout << endl <<
       strExec << " {-h|--help|-l|--list-devices|[COM PORT]}" << endl <<
       endl <<
@@ -209,22 +238,23 @@ ICECAdapter *CreateParser(cec_device_type_list typeList)
   if (!parser || parser->GetMinLibVersion() > CEC_TEST_CLIENT_VERSION)
   {
   #ifdef __WINDOWS__
-    cout << "Cannot load libcec.dll" << endl;
+    PrintToStdOut("Cannot load libcec.dll");
   #else
-    cout << "Cannot load libcec.so" << endl;
+    PrintToStdOut("Cannot load libcec.so");
   #endif
     return NULL;
   }
 
   CStdString strLog;
   strLog.Format("CEC Parser created - libcec version %d.%d", parser->GetLibVersionMajor(), parser->GetLibVersionMinor());
-  cout << strLog.c_str() << endl;
+  PrintToStdOut(strLog.c_str());
 
   return parser;
 }
 
 void ShowHelpConsole(void)
 {
+  CLockObject lock(&g_outputMutex);
   cout << endl <<
   "================================================================================" << endl <<
   "Available commands:" << endl <<
@@ -299,7 +329,7 @@ bool ProcessCommandON(ICECAdapter *parser, const string &command, string &argume
     }
     else
     {
-      cout << "invalid destination" << endl;
+      PrintToStdOut("invalid destination");
     }
   }
 
@@ -319,7 +349,7 @@ bool ProcessCommandSTANDBY(ICECAdapter *parser, const string &command, string &a
     }
     else
     {
-      cout << "invalid destination" << endl;
+      PrintToStdOut("invalid destination");
     }
   }
 
@@ -335,14 +365,14 @@ bool ProcessCommandPOLL(ICECAdapter *parser, const string &command, string &argu
     if (GetWord(arguments, strValue) && HexStrToInt(strValue, iValue) && iValue <= 0xF)
     {
       if (parser->PollDevice((cec_logical_address) iValue))
-        cout << "POLL message sent" << endl;
+        PrintToStdOut("POLL message sent");
       else
-        cout << "POLL message not sent" << endl;
+        PrintToStdOut("POLL message not sent");
       return true;
     }
     else
     {
-      cout << "invalid destination" << endl;
+      PrintToStdOut("invalid destination");
     }
   }
 
@@ -423,7 +453,7 @@ bool ProcessCommandOSD(ICECAdapter *parser, const string &command, string &argum
   return false;
 }
 
-bool ProcessCommandAS(ICECAdapter *parser, const string &command, string &arguments)
+bool ProcessCommandAS(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
 {
   if (command == "as")
   {
@@ -435,7 +465,7 @@ bool ProcessCommandAS(ICECAdapter *parser, const string &command, string &argume
 }
 
 
-bool ProcessCommandPING(ICECAdapter *parser, const string &command, string &arguments)
+bool ProcessCommandPING(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
 {
   if (command == "ping")
   {
@@ -446,39 +476,39 @@ bool ProcessCommandPING(ICECAdapter *parser, const string &command, string &argu
   return false;
 }
 
-bool ProcessCommandVOLUP(ICECAdapter *parser, const string &command, string &arguments)
+bool ProcessCommandVOLUP(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
 {
   if (command == "volup")
   {
     CStdString strLog;
     strLog.Format("volume up: %2X", parser->VolumeUp());
-    cout << strLog.c_str() << endl;
+    PrintToStdOut(strLog);
     return true;
   }
 
   return false;
 }
 
-bool ProcessCommandVOLDOWN(ICECAdapter *parser, const string &command, string &arguments)
+bool ProcessCommandVOLDOWN(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
 {
   if (command == "voldown")
   {
     CStdString strLog;
-    strLog.Format("volume up: %2X", parser->VolumeDown());
-    cout << strLog.c_str() << endl;
+    strLog.Format("volume down: %2X", parser->VolumeDown());
+    PrintToStdOut(strLog);
     return true;
   }
 
   return false;
 }
 
-bool ProcessCommandMUTE(ICECAdapter *parser, const string &command, string &arguments)
+bool ProcessCommandMUTE(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
 {
   if (command == "mute")
   {
     CStdString strLog;
     strLog.Format("mute: %2X", parser->MuteAudio());
-    cout << strLog.c_str() << endl;
+    PrintToStdOut(strLog);
     return true;
   }
 
@@ -500,7 +530,7 @@ bool ProcessCommandMON(ICECAdapter *parser, const string &command, string &argum
   return false;
 }
 
-bool ProcessCommandBL(ICECAdapter *parser, const string &command, string &arguments)
+bool ProcessCommandBL(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
 {
   if (command == "bl")
   {
@@ -527,7 +557,7 @@ bool ProcessCommandLANG(ICECAdapter *parser, const string &command, string &argu
           strLog.Format("menu language '%s'", language.language);
         else
           strLog = "failed!";
-        cout << strLog.c_str() << endl;
+        PrintToStdOut(strLog);
         return true;
       }
     }
@@ -549,7 +579,7 @@ bool ProcessCommandVEN(ICECAdapter *parser, const string &command, string &argum
         uint64_t iVendor = parser->GetDeviceVendorId((cec_logical_address) iDev);
         CStdString strLog;
         strLog.Format("vendor id: %06x", iVendor);
-        cout << strLog.c_str() << endl;
+        PrintToStdOut(strLog);
         return true;
       }
     }
@@ -569,7 +599,9 @@ bool ProcessCommandVER(ICECAdapter *parser, const string &command, string &argum
       if (iDev >= 0 && iDev < 15)
       {
         cec_version iVersion = parser->GetDeviceCecVersion((cec_logical_address) iDev);
-        cout << "CEC version " << parser->ToString(iVersion) << endl;
+        CStdString strLog;
+        strLog.Format("CEC version %s", parser->ToString(iVersion));
+        PrintToStdOut(strLog);
         return true;
       }
     }
@@ -589,8 +621,9 @@ bool ProcessCommandPOW(ICECAdapter *parser, const string &command, string &argum
       if (iDev >= 0 && iDev < 15)
       {
         cec_power_status iPower = parser->GetDevicePowerStatus((cec_logical_address) iDev);
-        cout << "power status: " << parser->ToString(iPower) << endl;
-
+        CStdString strLog;
+        strLog.Format("power status: %s", parser->ToString(iPower));
+        PrintToStdOut(strLog);
         return true;
       }
     }
@@ -610,7 +643,9 @@ bool ProcessCommandNAME(ICECAdapter *parser, const string &command, string &argu
       if (iDev >= 0 && iDev < 15)
       {
         cec_osd_name name = parser->GetDeviceOSDName((cec_logical_address)iDev);
-        cout << "OSD name of device " << iDev << " is '" << name.name << "'" << endl;
+        CStdString strLog;
+        strLog.Format("OSD name of device %d is '%s'", iDev, name.name);
+        PrintToStdOut(strLog);
       }
       return true;
     }
@@ -619,15 +654,19 @@ bool ProcessCommandNAME(ICECAdapter *parser, const string &command, string &argu
   return false;
 }
 
-bool ProcessCommandLAD(ICECAdapter *parser, const string &command, string &arguments)
+bool ProcessCommandLAD(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
 {
   if (command == "lad")
   {
-    cout << "listing active devices:" << endl;
+    CStdString strLog;
+    PrintToStdOut("listing active devices:");
     cec_logical_addresses addresses = parser->GetActiveDevices();
     for (uint8_t iPtr = 0; iPtr <= 11; iPtr++)
       if (addresses[iPtr])
-        cout << "logical address " << (int)iPtr << endl;
+      {
+        strLog.Format("logical address %X", (int)iPtr);
+        PrintToStdOut(strLog);
+      }
     return true;
   }
 
@@ -643,7 +682,11 @@ bool ProcessCommandAD(ICECAdapter *parser, const string &command, string &argume
     {
       int iDev = atoi(strDev);
       if (iDev >= 0 && iDev < 15)
-        cout << "logical address " << iDev << " is " << (parser->IsActiveDevice((cec_logical_address)iDev) ? "active" : "not active") << endl;
+      {
+        CStdString strLog;
+        strLog.Format("logical address %X is %s", iDev, (parser->IsActiveDevice((cec_logical_address)iDev) ? "active" : "not active"));
+        PrintToStdOut(strLog);
+      }
     }
   }
 
@@ -666,7 +709,9 @@ bool ProcessCommandAT(ICECAdapter *parser, const string &command, string &argume
         type = CEC_DEVICE_TYPE_RECORDING_DEVICE;
       else if (strType.Equals("t"))
         type = CEC_DEVICE_TYPE_TUNER;
-      cout << "device " << type << " is " << (parser->IsActiveDeviceType(type) ? "active" : "not active") << endl;
+      CStdString strLog;
+      strLog.Format("device %d is %s", type, (parser->IsActiveDeviceType(type) ? "active" : "not active"));
+      PrintToStdOut(strLog);
       return true;
     }
   }
@@ -674,19 +719,17 @@ bool ProcessCommandAT(ICECAdapter *parser, const string &command, string &argume
   return false;
 }
 
-bool ProcessCommandR(ICECAdapter *parser, const string &command, string &arguments)
+bool ProcessCommandR(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
 {
   if (command == "r")
   {
-    cout << "closing the connection" << endl;
+    PrintToStdOut("closing the connection");
     parser->Close();
-    FlushLog(parser);
 
-    cout << "opening a new connection" << endl;
+    PrintToStdOut("opening a new connection");
     parser->Open(g_strPort.c_str());
-    FlushLog(parser);
 
-    cout << "setting active source" << endl;
+    PrintToStdOut("setting active source");
     parser->SetActiveSource();
     return true;
   }
@@ -694,7 +737,7 @@ bool ProcessCommandR(ICECAdapter *parser, const string &command, string &argumen
   return false;
 }
 
-bool ProcessCommandH(ICECAdapter *parser, const string &command, string &arguments)
+bool ProcessCommandH(ICECAdapter * UNUSED(parser), const string &command, string & UNUSED(arguments))
 {
   if (command == "h" || command == "help")
   {
@@ -705,7 +748,7 @@ bool ProcessCommandH(ICECAdapter *parser, const string &command, string &argumen
   return false;
 }
 
-bool ProcessCommandLOG(ICECAdapter *parser, const string &command, string &arguments)
+bool ProcessCommandLOG(ICECAdapter * UNUSED(parser), const string &command, string &arguments)
 {
   if (command == "log")
   {
@@ -716,7 +759,9 @@ bool ProcessCommandLOG(ICECAdapter *parser, const string &command, string &argum
       if (iNewLevel >= CEC_LOG_ERROR && iNewLevel <= CEC_LOG_ALL)
       {
         g_cecLogLevel = iNewLevel;
-        cout << "log level changed to " << strLevel.c_str() << endl;
+        CStdString strLog;
+        strLog.Format("log level changed to %s", strLevel.c_str());
+        PrintToStdOut(strLog);
         return true;
       }
     }
@@ -725,17 +770,18 @@ bool ProcessCommandLOG(ICECAdapter *parser, const string &command, string &argum
   return false;
 }
 
-bool ProcessCommandSCAN(ICECAdapter *parser, const string &command, string &arguments)
+bool ProcessCommandSCAN(ICECAdapter *parser, const string &command, string & UNUSED(arguments))
 {
   if (command == "scan")
   {
-    cout << "CEC bus information" << endl;
-    cout << "===================" << endl;
+    PrintToStdOut("CEC bus information");
+    PrintToStdOut("===================");
     cec_logical_addresses addresses = parser->GetActiveDevices();
     for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
     {
       if (addresses[iPtr])
       {
+        CStdString strLog;
         uint64_t iVendorId        = parser->GetDeviceVendorId((cec_logical_address)iPtr);
         bool     bActive          = parser->IsActiveSource((cec_logical_address)iPtr);
         uint16_t iPhysicalAddress = parser->GetDevicePhysicalAddress((cec_logical_address)iPtr);
@@ -748,16 +794,17 @@ bool ProcessCommandSCAN(ICECAdapter *parser, const string &command, string &argu
         lang.device = CECDEVICE_UNKNOWN;
         parser->GetDeviceMenuLanguage((cec_logical_address)iPtr, &lang);
 
-        cout << "device #" << (int)iPtr << ": " << parser->ToString((cec_logical_address)iPtr) << endl;
-        cout << "address:       " << strAddr.c_str() << endl;
-        cout << "active source: " << (bActive ? "yes" : "no") << endl;
-        cout << "vendor:        " << parser->ToString((cec_vendor_id)iVendorId) << endl;
-        cout << "osd string:    " << osdName.name << endl;
-        cout << "CEC version:   " << parser->ToString(iCecVersion) << endl;
-        cout << "power status:  " << parser->ToString(power) << endl;
+        strLog.AppendFormat("device #%X: %s\n", (int)iPtr, parser->ToString((cec_logical_address)iPtr));
+        strLog.AppendFormat("address:       %s\n", strAddr.c_str());
+        strLog.AppendFormat("active source: %s\n", (bActive ? "yes" : "no"));
+        strLog.AppendFormat("vendor:        %s\n", parser->ToString((cec_vendor_id)iVendorId));
+        strLog.AppendFormat("osd string:    %s\n", osdName.name);
+        strLog.AppendFormat("CEC version:   %s\n", parser->ToString(iCecVersion));
+        strLog.AppendFormat("power status:  %s\n", parser->ToString(power));
         if ((uint8_t)lang.device == iPtr)
-          cout << "language:      " << lang.language << endl;
-        cout << endl;
+          strLog.AppendFormat("language:      %s\n", lang.language);
+        strLog.append("\n");
+        PrintToStdOut(strLog);
       }
     }
     return true;
@@ -972,6 +1019,10 @@ int main (int argc, char *argv[])
 #else
     cout << "Cannot load libcec.so" << endl;
 #endif
+
+    if (parser)
+      UnloadLibCec(parser);
+
     return 1;
   }
 
@@ -1014,40 +1065,33 @@ int main (int argc, char *argv[])
     }
   }
 
+  EnableCallbacks(parser);
+
   parser->SetHDMIPort(g_iBaseDevice, g_iHDMIPort);
-  cout << "opening a connection to the CEC adapter..." << endl;
+  PrintToStdOut("opening a connection to the CEC adapter...");
 
   if (!parser->Open(g_strPort.c_str()))
   {
-    cout << "unable to open the device on port " << g_strPort << endl;
-    FlushLog(parser);
+    CStdString strLog;
+    strLog.Format("unable to open the device on port %s");
+    PrintToStdOut(strLog);
     UnloadLibCec(parser);
     return 1;
   }
 
   if (!g_bSingleCommand)
   {
-    FlushLog(parser);
-    cout << "cec device opened" << endl;
+    PrintToStdOut("cec device opened");
 
     parser->PowerOnDevices(CECDEVICE_TV);
-    FlushLog(parser);
-
     parser->SetActiveSource();
-    FlushLog(parser);
 
-    cout << "waiting for input" << endl;
+    PrintToStdOut("waiting for input");
   }
 
   bool bContinue(true);
   while (bContinue)
   {
-    FlushLog(parser);
-
-    /* just ignore the command buffer and clear it */
-    cec_command dummy;
-    while (parser && parser->GetNextCommand(&dummy)) {}
-
     string input;
     getline(cin, input);
     cin.clear();
@@ -1055,7 +1099,7 @@ int main (int argc, char *argv[])
     if (ProcessConsoleCommand(parser, input) && !g_bSingleCommand)
     {
       if (!input.empty())
-        cout << "waiting for input" << endl;
+        PrintToStdOut("waiting for input");
     }
     else
       bContinue = false;
@@ -1068,7 +1112,6 @@ int main (int argc, char *argv[])
     parser->StandbyDevices(CECDEVICE_BROADCAST);
 
   parser->Close();
-  FlushLog(parser);
   UnloadLibCec(parser);
 
   if (g_logOutput.is_open())