LibCecSharp: fixed 14 char long OSD name (wasn't \0 terminated)
[deb_libcec.git] / src / LibCecSharp / LibCecSharp.cpp
index 3aa1253512034786373e2ad432bfeff429ae623a..0bae3eaca39e9d820f49b605968b6e39d78fa580 100644 (file)
@@ -1,40 +1,36 @@
 /*
- * This file is part of the libCEC(R) library.
- *
- * libCEC(R) is Copyright (C) 2011 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 "stdafx.h"
-#include <windows.h>
-#include <vcclr.h>
-#include <msclr/marshal.h>
-#include <cec.h>
+* This file is part of the libCEC(R) library.
+*
+* 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.
+*
+* 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 "CecSharpTypes.h"
 #using <System.dll>
 
 using namespace System;
@@ -42,963 +38,802 @@ using namespace System::Runtime::InteropServices;
 using namespace CEC;
 using namespace msclr::interop;
 
-public enum class CecDeviceType
+namespace CecSharp
 {
-  Tv              = 0,
-  RecordingDevice = 1,
-  Reserved        = 2,
-  Tuner           = 3,
-  PlaybackDevice  = 4,
-  AudioSystem     = 5
-};
-
-public enum class CecLogLevel
-{
-  None    = 0,
-  Error   = 1,
-  Warning = 2,
-  Notice  = 4,
-  Traffic = 8,
-  Debug   = 16,
-  All     = 31
-};
-
-public enum class CecLogicalAddress
-{
-  Unknown          = -1, //not a valid logical address
-  Tv               = 0,
-  RecordingDevice1 = 1,
-  RecordingDevice2 = 2,
-  Tuner1           = 3,
-  PlaybackDevice1  = 4,
-  AudioSystem      = 5,
-  Tuner2           = 6,
-  Tuner3           = 7,
-  PlaybackDevice2  = 8,
-  RecordingDevice3 = 9,
-  Tuner4           = 10,
-  PlaybackDevice3  = 11,
-  Reserved1        = 12,
-  Reserved2        = 13,
-  FreeUse          = 14,
-  Unregistered     = 15,
-  Broadcast        = 15
-};
-
-public enum class CecPowerStatus
-{
-  On                      = 0x00,
-  Standby                 = 0x01,
-  InTransitionStandbyToOn = 0x02,
-  InTransitionOnToStandby = 0x03,
-  Unknown                 = 0x99
-};
-
-public enum class CecVersion
-{
-  Unknown = 0x00,
-  V1_2    = 0x01,
-  V1_2A   = 0x02,
-  V1_3    = 0x03,
-  V1_3A   = 0x04,
-  V1_4    = 0x05
-};
-
-public enum class CecDisplayControl
-{
-  DisplayForDefaultTime = 0x00,
-  DisplayUntilCleared   = 0x40,
-  ClearPreviousMessage  = 0x80,
-  ReservedForFutureUse  = 0xC0
-};
+  /// <summary>
+  /// Create a LibCecSharp instance and pass the configuration as argument.
+  /// Then call Open() to open a connection to the adapter. Close() closes the
+  /// connection.
+  ///
+  /// libCEC can send commands to other devices on the CEC bus via the methods
+  /// on this interface, and all commands that libCEC received are sent back
+  /// to the application via callback methods. The callback methods can be
+  /// found in CecSharpTypes.h, CecCallbackMethods.
+  /// </summary>
+  public ref class LibCecSharp : public CecCallbackMethods
+  {
+  public:
+    /// <summary>
+    /// Create a new LibCecSharp instance.
+    /// </summary>
+    /// <param name="config">The configuration to pass to libCEC.</param>
+    LibCecSharp(LibCECConfiguration ^config)
+    {
+      m_callbacks = config->Callbacks;
+      CecCallbackMethods::EnableCallbacks(m_callbacks);
+      if (!InitialiseLibCec(config))
+        throw gcnew Exception("Could not initialise LibCecSharp");
+    }
 
-public enum class CecMenuState
-{
-  Activated   = 0,
-  Deactivated = 1
-};
+    ~LibCecSharp(void)
+    {
+      Close();
+      m_libCec = NULL;
+    }
 
-public enum class CecDeckControlMode
-{
-  SkipForwardWind   = 1,
-  SkipReverseRewind = 2,
-  Stop              = 3,
-  Eject             = 4
-};
+    /// <summary>
+    /// Try to find all connected CEC adapters.
+    /// </summary>
+    /// <param name="path">The path filter for adapters. Leave empty to return all adapters.</param>
+    /// <returns>The adapters that were found.</returns>
+    array<CecAdapter ^> ^ FindAdapters(String ^ path)
+    {
+      cec_adapter *devices = new cec_adapter[10];
 
-public enum class CecDeckInfo
-{
-  Play               = 0x11,
-  Record             = 0x12,
-  Reverse            = 0x13,
-  Still              = 0x14,
-  Slow               = 0x15,
-  SlowReverse        = 0x16,
-  FastForward        = 0x17,
-  FastReverse        = 0x18,
-  NoMedia            = 0x19,
-  Stop               = 0x1A,
-  SkipForwardWind    = 0x1B,
-  SkipReverseRewind  = 0x1C,
-  IndexSearchForward = 0x1D,
-  IndexSearchReverse = 0x1E,
-  OtherStatus        = 0x1F
-};
-
-public enum class CecUserControlCode
-{
-  Select                      = 0x00,
-  Up                          = 0x01,
-  Down                        = 0x02,
-  Left                        = 0x03,
-  Right                       = 0x04,
-  RightUp                     = 0x05,
-  RightDown                   = 0x06,
-  LeftUp                      = 0x07,
-  LeftDown                    = 0x08,
-  RootMenu                    = 0x09,
-  SetupMenu                   = 0x0A,
-  ContentsMenu                = 0x0B,
-  FavoriteMenu                = 0x0C,
-  Exit                        = 0x0D,
-  Number0                     = 0x20,
-  Number1                     = 0x21,
-  Number2                     = 0x22,
-  Number3                     = 0x23,
-  Number4                     = 0x24,
-  Number5                     = 0x25,
-  Number6                     = 0x26,
-  Number7                     = 0x27,
-  Number8                     = 0x28,
-  Number9                     = 0x29,
-  Dot                         = 0x2A,
-  Enter                       = 0x2B,
-  Clear                       = 0x2C,
-  NextFavorite                = 0x2F,
-  ChannelUp                   = 0x30,
-  ChannelDown                 = 0x31,
-  PreviousChannel             = 0x32,
-  SoundSelect                 = 0x33,
-  InputSelect                 = 0x34,
-  DisplayInformation          = 0x35,
-  Help                        = 0x36,
-  PageUp                      = 0x37,
-  PageDown                    = 0x38,
-  Power                       = 0x40,
-  VolumeUp                    = 0x41,
-  VolumeDown                  = 0x42,
-  Mute                        = 0x43,
-  Play                        = 0x44,
-  Stop                        = 0x45,
-  Pause                       = 0x46,
-  Record                      = 0x47,
-  Rewind                      = 0x48,
-  FastForward                 = 0x49,
-  Eject                       = 0x4A,
-  Forward                     = 0x4B,
-  Backward                    = 0x4C,
-  StopRecord                  = 0x4D,
-  PauseRecord                 = 0x4E,
-  Angle                       = 0x50,
-  SubPicture                  = 0x51,
-  VideoOnDemand               = 0x52,
-  ElectronicProgramGuide      = 0x53,
-  TimerProgramming            = 0x54,
-  InitialConfiguration        = 0x55,
-  PlayFunction                = 0x60,
-  PausePlayFunction           = 0x61,
-  RecordFunction              = 0x62,
-  PauseRecordFunction         = 0x63,
-  StopFunction                = 0x64,
-  MuteFunction                = 0x65,
-  RestoreVolumeFunction       = 0x66,
-  TuneFunction                = 0x67,
-  SelectMediaFunction         = 0x68,
-  SelectAVInputFunction       = 0x69,
-  SelectAudioInputFunction    = 0x6A,
-  PowerToggleFunction         = 0x6B,
-  PowerOffFunction            = 0x6C,
-  PowerOnFunction             = 0x6D,
-  F1Blue                      = 0x71,
-  F2Red                       = 0X72,
-  F3Green                     = 0x73,
-  F4Yellow                    = 0x74,
-  F5                          = 0x75,
-  Data                        = 0x76,
-  Max                         = 0x76,
-  Unknown
-};
-
-public enum class CecVendorId
-{
-  Samsung   = 0x00F0,
-  LG        = 0xE091,
-  Panasonic = 0x8045,
-  Pioneer   = 0xE036,
-  Onkyo     = 0x09B0,
-  Yamaha    = 0xA0DE,
-  Philips   = 0x903E,
-  Unknown   = 0
-};
-
-public enum class CecAudioStatus
-{
-  MuteStatusMask      = 0x80,
-  VolumeStatusMask    = 0x7F,
-  VolumeMin           = 0x00,
-  VolumeMax           = 0x64,
-  VolumeStatusUnknown = 0x7F
-};
-
-public enum class CecOpcode
-{
-  ActiveSource                  = 0x82,
-  ImageViewOn                   = 0x04,
-  TextViewOn                    = 0x0D,
-  InactiveSource                = 0x9D,
-  RequestActiveSource           = 0x85,
-  RoutingChange                 = 0x80,
-  RoutingInformation            = 0x81,
-  SetStreamPath                 = 0x86,
-  Standby                       = 0x36,
-  RecordOff                     = 0x0B,
-  RecordOn                      = 0x09,
-  RecordStatus                  = 0x0A,
-  RecordTvScreen                = 0x0F,
-  ClearAnalogueTimer            = 0x33,
-  ClearDigitalTimer             = 0x99,
-  ClearExternalTimer            = 0xA1,
-  SetAnalogueTimer              = 0x34,
-  SetDigitalTimer               = 0x97,
-  SetExternalTimer              = 0xA2,
-  SetTimerProgramTitle          = 0x67,
-  TimerClearedStatus            = 0x43,
-  TimerStatus                   = 0x35,
-  CecVersion                    = 0x9E,
-  GetCecVersion                 = 0x9F,
-  GivePhysicalAddress           = 0x83,
-  GetMenuLanguage               = 0x91,
-  ReportPhysicalAddress         = 0x84,
-  SetMenuLanguage               = 0x32,
-  DeckControl                   = 0x42,
-  DeckStatus                    = 0x1B,
-  GiveDeckStatus                = 0x1A,
-  Play                          = 0x41,
-  GiveTunerDeviceStatus         = 0x08,
-  SelectAnalogueService         = 0x92,
-  SelectDigtalService           = 0x93,
-  TunerDeviceStatus             = 0x07,
-  TunerStepDecrement            = 0x06,
-  TunerStepIncrement            = 0x05,
-  DeviceVendorId                = 0x87,
-  GiveDeviceVendorId            = 0x8C,
-  VendorCommand                 = 0x89,
-  VendorCommandWithId           = 0xA0,
-  VendorRemoteButtonDown        = 0x8A,
-  VendorRemoteButtonUp          = 0x8B,
-  SetOsdString                  = 0x64,
-  GiveOsdName                   = 0x46,
-  SetOsdName                    = 0x47,
-  MenuRequest                   = 0x8D,
-  MenuStatus                    = 0x8E,
-  UserControlPressed            = 0x44,
-  UserControlRelease            = 0x45,
-  GiveDevicePowerStatus         = 0x8F,
-  ReportPowerStatus             = 0x90,
-  FeatureAbort                  = 0x00,
-  Abort                         = 0xFF,
-  GiveAudioStatus               = 0x71,
-  GiveSystemAudioMode           = 0x7D,
-  ReportAudioStatus             = 0x7A,
-  SetSystemAudioMode            = 0x72,
-  SystemAudioModeRequest        = 0x70,
-  SystemAudioModeStatus         = 0x7E,
-  SetAudioRate                  = 0x9A,
-  /* when this opcode is set, no opcode will be sent to the device. this is one of the reserved numbers */
-  None                          = 0xFD
-};
-
-public enum class CecSystemAudioStatus
-{
-  Off = 0,
-  On  = 1
-};
+      marshal_context ^ context = gcnew marshal_context();
+      const char* strPathC = path->Length > 0 ? context->marshal_as<const char*>(path) : NULL;
 
-public ref class CecAdapter
-{
-public:
-  CecAdapter(String ^ strPath, String ^ strComPort)
-  {
-    Path = strPath;
-    ComPort = strComPort;
-  }
+      uint8_t iDevicesFound = m_libCec->FindAdapters(devices, 10, NULL);
 
-  property String ^ Path;
-  property String ^ ComPort;
-};
+      array<CecAdapter ^> ^ adapters = gcnew array<CecAdapter ^>(iDevicesFound);
+      for (unsigned int iPtr = 0; iPtr < iDevicesFound; iPtr++)
+        adapters[iPtr] = gcnew CecAdapter(gcnew String(devices[iPtr].path), gcnew String(devices[iPtr].comm));
 
-public ref class CecDeviceTypeList
-{
-public:
-  CecDeviceTypeList(void)
-  {
-    Types = gcnew array<CecDeviceType>(5);
-    for (unsigned int iPtr = 0; iPtr < 5; iPtr++)
-      Types[iPtr] = CecDeviceType::Reserved;
-  }
+      delete devices;
+      delete context;
+      return adapters;
+    }
 
-  property array<CecDeviceType> ^ Types;
-};
+    /// <summary>
+    /// Open a connection to the CEC adapter.
+    /// </summary>
+    /// <param name="strPort">The COM port of the adapter</param>
+    /// <param name="iTimeoutMs">Connection timeout in milliseconds</param>
+    /// <returns>True when a connection was opened, false otherwise.</returns>
+    bool Open(String ^ strPort, int iTimeoutMs)
+    {
+      CecCallbackMethods::EnableCallbacks(m_callbacks);
+      EnableCallbacks(m_callbacks);
+      marshal_context ^ context = gcnew marshal_context();
+      const char* strPortC = context->marshal_as<const char*>(strPort);
+      bool bReturn = m_libCec->Open(strPortC, iTimeoutMs);
+      delete context;
+      return bReturn;
+    }
 
-public ref class CecLogicalAddresses
-{
-public:
-  CecLogicalAddresses(void)
-  {
-    Addresses = gcnew array<CecLogicalAddress>(16);
-    for (unsigned int iPtr = 0; iPtr < 16; iPtr++)
-      Addresses[iPtr] = CecLogicalAddress::Unregistered;
-  }
+    /// <summary>
+    /// Close the connection to the CEC adapter
+    /// </summary>
+    void Close(void)
+    {
+      DisableCallbacks();
+      m_libCec->Close();
+    }
 
-  bool IsSet(CecLogicalAddress iAddress)
-  {
-    return Addresses[(unsigned int)iAddress] != CecLogicalAddress::Unregistered;
-  }
+    /// <summary>
+    /// Disable all calls to callback methods.
+    /// </summary>
+    virtual void DisableCallbacks(void) override
+    {
+      // delete the callbacks, since these might already have been destroyed in .NET
+      CecCallbackMethods::DisableCallbacks();
+      if (m_libCec)
+        m_libCec->EnableCallbacks(NULL, NULL);
+    }
 
-  property array<CecLogicalAddress> ^ Addresses;
-};
+    /// <summary>
+    /// Enable or change the callback methods that libCEC uses to send changes to the client application.
+    /// </summary>
+    /// <param name="callbacks">The new callback methods to use.</param>
+    /// <returns>True when the callbacks were changed, false otherwise</returns>
+    virtual bool EnableCallbacks(CecCallbackMethods ^ callbacks) override
+    {
+      if (m_libCec && CecCallbackMethods::EnableCallbacks(callbacks))
+        return m_libCec->EnableCallbacks((void*)GetCallbackPtr(), &g_cecCallbacks);
 
-public ref class CecDatapacket
-{
-public:
-  CecDatapacket(void)
-  {
-    Data = gcnew array<uint8_t>(100);
-    Size = 0;
-  }
+      return false;
+    }
 
-  void PushBack(uint8_t data)
-  {
-    if (Size < 100)
+    /// <summary>
+    /// Sends a ping command to the adapter, to check if it's responding.
+    /// </summary>
+    /// <returns>True when the ping was succesful, false otherwise</returns>
+    bool PingAdapter(void)
     {
-      Data[Size] = data;
-      Size++;
+      return m_libCec->PingAdapter();
     }
-  }
 
-  property array<uint8_t> ^ Data;
-  property uint8_t          Size;
-};
+    /// <summary>
+    /// Start the bootloader of the CEC adapter. Closes the connection when successful.
+    /// </summary>
+    /// <returns>True when the command was sent successfully, false otherwise.</returns>
+    bool StartBootloader(void)
+    {
+      return m_libCec->StartBootloader();
+    }
 
-public ref class CecCommand
-{
-public:
-  CecCommand(CecLogicalAddress iInitiator, CecLogicalAddress iDestination, bool bAck, bool bEom, CecOpcode iOpcode, int32_t iTransmitTimeout)
-  {
-    Initiator       = iInitiator;
-    Destination     = iDestination;
-    Ack             = bAck;
-    Eom             = bEom;
-    Opcode          = iOpcode;
-    OpcodeSet       = true;
-    TransmitTimeout = iTransmitTimeout;
-    Parameters      = gcnew CecDatapacket;
-    Empty           = false;
-  }
-
-  CecCommand(void)
-  {
-    Initiator       = CecLogicalAddress::Unknown;
-    Destination     = CecLogicalAddress::Unknown;
-    Ack             = false;
-    Eom             = false;
-    Opcode          = CecOpcode::None;
-    OpcodeSet       = false;
-    TransmitTimeout = 0;
-    Parameters      = gcnew CecDatapacket;
-    Empty           = true;
-  }
-
-  void PushBack(uint8_t data)
-  {
-    if (Initiator == CecLogicalAddress::Unknown && Destination == CecLogicalAddress::Unknown)
+    /// <summary>
+    /// Transmit a raw CEC command over the CEC line.
+    /// </summary>
+    /// <param name="command">The command to transmit</param>
+    /// <returns>True when the data was sent and acked, false otherwise.</returns>
+    bool Transmit(CecCommand ^ command)
     {
-      Initiator   = (CecLogicalAddress) (data >> 4);
-      Destination = (CecLogicalAddress) (data & 0xF);
+      cec_command ccommand;
+      cec_command::Format(ccommand, (cec_logical_address)command->Initiator, (cec_logical_address)command->Destination, (cec_opcode)command->Opcode);
+      ccommand.transmit_timeout = command->TransmitTimeout;
+      ccommand.eom              = command->Eom;
+      ccommand.ack              = command->Ack;
+      for (unsigned int iPtr = 0; iPtr < command->Parameters->Size; iPtr++)
+        ccommand.parameters.PushBack(command->Parameters->Data[iPtr]);
+
+      return m_libCec->Transmit(ccommand);
     }
-    else if (!OpcodeSet)
+
+    /// <summary>
+    /// Change the logical address on the CEC bus of the CEC adapter. libCEC automatically assigns a logical address, and this method is only available for debugging purposes.
+    /// </summary>
+    /// <param name="logicalAddress">The CEC adapter's new logical address.</param>
+    /// <returns>True when the logical address was set successfully, false otherwise.</returns>
+    bool SetLogicalAddress(CecLogicalAddress logicalAddress)
     {
-      OpcodeSet = true;
-      Opcode    = (CecOpcode)data;
+      return m_libCec->SetLogicalAddress((cec_logical_address) logicalAddress);
     }
-    else
+
+    /// <summary>
+    /// Change the physical address (HDMI port) of the CEC adapter. libCEC will try to autodetect the physical address when connecting. If it did, it's set in libcec_configuration.
+    /// </summary>
+    /// <param name="physicalAddress">The CEC adapter's new physical address.</param>
+    /// <returns>True when the physical address was set successfully, false otherwise.</returns>
+    bool SetPhysicalAddress(uint16_t physicalAddress)
     {
-      Parameters->PushBack(data);
+      return m_libCec->SetPhysicalAddress(physicalAddress);
     }
-  }
 
-  property bool               Empty;
-  property CecLogicalAddress  Initiator;
-  property CecLogicalAddress  Destination;
-  property bool               Ack;
-  property bool               Eom;
-  property CecOpcode          Opcode;
-  property CecDatapacket ^    Parameters;
-  property bool               OpcodeSet;
-  property int32_t            TransmitTimeout;
-};
+    /// <summary>
+    /// Power on the given CEC capable devices. If CECDEVICE_BROADCAST is used, then wakeDevice in libcec_configuration will be used.
+    /// </summary>
+    /// <param name="logicalAddress">The logical address to power on.</param>
+    /// <returns>True when the command was sent succesfully, false otherwise.</returns>
+    bool PowerOnDevices(CecLogicalAddress logicalAddress)
+    {
+      return m_libCec->PowerOnDevices((cec_logical_address) logicalAddress);
+    }
 
-public ref class CecKeypress
-{
-public:
-  CecKeypress(int iKeycode, unsigned int iDuration)
-  {
-    Keycode  = iKeycode;
-    Duration = iDuration;
-    Empty    = false;
-  }
+    /// <summary>
+    /// Put the given CEC capable devices in standby mode. If CECDEVICE_BROADCAST is used, then standbyDevices in libcec_configuration will be used.
+    /// </summary>
+    /// <param name="logicalAddress">The logical address of the device to put in standby.</param>
+    /// <returns>True when the command was sent succesfully, false otherwise.</returns>
+    bool StandbyDevices(CecLogicalAddress logicalAddress)
+    {
+      return m_libCec->StandbyDevices((cec_logical_address) logicalAddress);
+    }
 
-  CecKeypress(void)
-  {
-    Keycode  = 0;
-    Duration = 0;
-    Empty    = true;
-  }
+    /// <summary>
+    /// Sends a POLL message to a device, to check if it's present and responding.
+    /// </summary>
+    /// <param name="logicalAddress">The device to send the message to.</param>
+    /// <returns>True if the POLL was acked, false otherwise.</returns>
+    bool PollDevice(CecLogicalAddress logicalAddress)
+    {
+      return m_libCec->PollDevice((cec_logical_address) logicalAddress);
+    }
 
-  property bool         Empty;
-  property int          Keycode;
-  property unsigned int Duration;
-};
+    /// <summary>
+    /// Change the active source to a device type handled by libCEC. Use CEC_DEVICE_TYPE_RESERVED to make the default type used by libCEC active.
+    /// </summary>
+    /// <param name="type">The new active source. Use CEC_DEVICE_TYPE_RESERVED to use the primary type</param>
+    /// <returns>True when the command was sent succesfully, false otherwise.</returns>
+    bool SetActiveSource(CecDeviceType type)
+    {
+      return m_libCec->SetActiveSource((cec_device_type) type);
+    }
 
-public ref class CecLogMessage
-{
-public:
-  CecLogMessage(String ^ strMessage, CecLogLevel iLevel, int64_t iTime)
-  {
-    Message = strMessage;
-    Level   = iLevel;
-    Time    = iTime;
-    Empty   = false;
-  }
+    /// <summary>
+    /// Change the deck control mode, if this adapter is registered as playback or recording device.
+    /// </summary>
+    /// <param name="mode">The new control mode.</param>
+    /// <param name="sendUpdate">True to send the new status over the CEC line.</param>
+    /// <returns>True if set, false otherwise.</returns>
+    bool SetDeckControlMode(CecDeckControlMode mode, bool sendUpdate)
+    {
+      return m_libCec->SetDeckControlMode((cec_deck_control_mode) mode, sendUpdate);
+    }
 
-  CecLogMessage(void)
-  {
-    Message = "";
-    Level   = CecLogLevel::None;
-    Time    = 0;
-    Empty   = true;
-  }
-
-  property bool        Empty;
-  property String ^    Message;
-  property CecLogLevel Level;
-  property int64_t     Time;
-};
-
-public ref class CecCallbackMethods
-{
-public:
-  virtual int ReceiveLogMessage(CecLogMessage ^ message)
-  {
-    return 0;
-  }
+    /// <summary>
+    /// Change the deck info, if this adapter is a playback or recording device.
+    /// </summary>
+    /// <param name="info">The new deck info.</param>
+    /// <param name="sendUpdate">True to send the new status over the CEC line.</param>
+    /// <returns>True if set, false otherwise.</returns>
+    bool SetDeckInfo(CecDeckInfo info, bool sendUpdate)
+    {
+      return m_libCec->SetDeckInfo((cec_deck_info) info, sendUpdate);
+    }
 
-  virtual int ReceiveKeypress(CecKeypress ^ key)
-  {
-    return 0;
-  }
+    /// <summary>
+    /// Broadcast a message that notifies connected CEC capable devices that this device is no longer the active source.
+    /// </summary>
+    /// <returns>True when the command was sent succesfully, false otherwise.</returns>
+    bool SetInactiveView(void)
+    {
+      return m_libCec->SetInactiveView();
+    }
 
-  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;
-}
+    /// <summary>
+    /// Change the menu state. This value is already changed by libCEC automatically if a device is (de)activated.
+    /// </summary>
+    /// <param name="state">The new state.</param>
+    /// <param name="sendUpdate">True to send the new status over the CEC line.</param>
+    /// <returns>True if set, false otherwise.</returns>
+    bool SetMenuState(CecMenuState state, bool sendUpdate)
+    {
+      return m_libCec->SetMenuState((cec_menu_state) state, sendUpdate);
+    }
 
-int CecKeyPressCB(void *cbParam, const cec_keypress &key)
-{
-  if (g_keyCB)
-    return g_keyCB(key);
-  return 0;
-}
+    /// <summary>
+    /// Display a message on the device with the given logical address. Not supported by most TVs.
+    /// </summary>
+    /// <param name="logicalAddress">The logical address of the device to display the message on.</param>
+    /// <param name="duration">The duration of the message</param>
+    /// <param name="message">The message to display.</param>
+    /// <returns>True when the command was sent, false otherwise.</returns>
+    bool SetOSDString(CecLogicalAddress logicalAddress, CecDisplayControl duration, String ^ message)
+    {
+      marshal_context ^ context = gcnew marshal_context();
+      const char* strMessageC = context->marshal_as<const char*>(message);
 
-int CecCommandCB(void *cbParam, const cec_command &command)
-{
-  if (g_commandCB)
-    return g_commandCB(command);
-  return 0;
-}
+      bool bReturn = m_libCec->SetOSDString((cec_logical_address) logicalAddress, (cec_display_control) duration, strMessageC);
 
-#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 &);
+      delete context;
+      return bReturn;
+    }
 
-public ref class LibCecSharp
-{
-public:
-  LibCecSharp(String ^ strDeviceName, CecDeviceTypeList ^ deviceTypes)
-  {
-    marshal_context ^ context = gcnew marshal_context();
-    m_bHasCallbacks = false;
-    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;
-
-    delete context;
-  }
-   
-   ~LibCecSharp(void)
-   {
-     CECDestroy(m_libCec);
-     DestroyDelegates();
-     m_libCec = NULL;
-   }
-
-protected:
-   !LibCecSharp(void)
-   {
-     CECDestroy(m_libCec);
-     DestroyDelegates();
-     m_libCec = NULL;
-   }
-
-public:
-  array<CecAdapter ^> ^ FindAdapters(String ^ path)
-  {
-    cec_adapter *devices = new cec_adapter[10];
+    /// <summary>
+    /// Enable or disable monitoring mode, for debugging purposes. If monitoring mode is enabled, libCEC won't respond to any command, but only log incoming data.
+    /// </summary>
+    /// <param name="enable">True to enable, false to disable.</param>
+    /// <returns>True when switched successfully, false otherwise.</returns>
+    bool SwitchMonitoring(bool enable)
+    {
+      return m_libCec->SwitchMonitoring(enable);
+    }
 
-    marshal_context ^ context = gcnew marshal_context();
-    const char* strPathC = path->Length > 0 ? context->marshal_as<const char*>(path) : NULL;
+    /// <summary>
+    /// Get the CEC version of the device with the given logical address
+    /// </summary>
+    /// <param name="logicalAddress">The logical address of the device to get the CEC version for.</param>
+    /// <returns>The version or CEC_VERSION_UNKNOWN when the version couldn't be fetched.</returns>
+    CecVersion GetDeviceCecVersion(CecLogicalAddress logicalAddress)
+    {
+      return (CecVersion) m_libCec->GetDeviceCecVersion((cec_logical_address) logicalAddress);
+    }
 
-    uint8_t iDevicesFound = m_libCec->FindAdapters(devices, 10, NULL);
+    /// <summary>
+    /// Get the menu language of the device with the given logical address
+    /// </summary>
+    /// <param name="logicalAddress">The logical address of the device to get the menu language for.</param>
+    /// <returns>The requested menu language.</returns>
+    String ^ GetDeviceMenuLanguage(CecLogicalAddress logicalAddress)
+    {
+      cec_menu_language lang;
+      if (m_libCec->GetDeviceMenuLanguage((cec_logical_address) logicalAddress, &lang))
+      {
+        return gcnew String(lang.language);
+      }
 
-    array<CecAdapter ^> ^ adapters = gcnew array<CecAdapter ^>(iDevicesFound);
-    for (unsigned int iPtr = 0; iPtr < iDevicesFound; iPtr++)
-      adapters[iPtr] = gcnew CecAdapter(gcnew String(devices[iPtr].path), gcnew String(devices[iPtr].comm));
+      return gcnew String("");
+    }
 
-    delete devices;
-    delete context;
-    return adapters;
-  }
+    /// <summary>
+    /// Get the vendor ID of the device with the given logical address.
+    /// </summary>
+    /// <param name="logicalAddress">The logical address of the device to get the vendor ID for.</param>
+    /// <returns>The vendor ID or 0 if it wasn't found.</returns>
+    CecVendorId GetDeviceVendorId(CecLogicalAddress logicalAddress)
+    {
+      return (CecVendorId)m_libCec->GetDeviceVendorId((cec_logical_address) logicalAddress);
+    }
 
-  bool Open(String ^ strPort, int iTimeoutMs)
-  {
-    marshal_context ^ context = gcnew marshal_context();
-    const char* strPortC = context->marshal_as<const char*>(strPort);
-    bool bReturn = m_libCec->Open(strPortC, iTimeoutMs);
-    delete context;
-    return bReturn;
-  }
-
-  void Close(void)
-  {
-    m_libCec->Close();
-  }
+    /// <summary>
+    /// Get the power status of the device with the given logical address.
+    /// </summary>
+    /// <param name="logicalAddress">The logical address of the device to get the power status for.</param>
+    /// <returns>The power status or CEC_POWER_STATUS_UNKNOWN if it wasn't found.</returns>
+    CecPowerStatus GetDevicePowerStatus(CecLogicalAddress logicalAddress)
+    {
+      return (CecPowerStatus) m_libCec->GetDevicePowerStatus((cec_logical_address) logicalAddress);
+    }
 
-  bool EnableCallbacks(CecCallbackMethods ^ callbacks)
-  {
-    if (m_libCec && !m_bHasCallbacks)
+    /// <summary>
+    /// Tell libCEC to poll for active devices on the bus.
+    /// </summary>
+    void RescanActiveDevices(void)
     {
-      m_bHasCallbacks = true;
-      m_callbacks = callbacks;
-      return m_libCec->EnableCallbacks(NULL, &g_cecCallbacks);
+      m_libCec->RescanActiveDevices();
     }
 
-    return false;
-  }
+    /// <summary>
+    /// Get the logical addresses of the devices that are active on the bus, including those handled by libCEC.
+    /// </summary>
+    /// <returns>The logical addresses of the active devices</returns>
+    CecLogicalAddresses ^ GetActiveDevices(void)
+    {
+      CecLogicalAddresses ^ retVal = gcnew CecLogicalAddresses();
+      unsigned int iDevices = 0;
 
-  bool PingAdapter(void)
-  {
-    return m_libCec->PingAdapter();
-  }
+      cec_logical_addresses activeDevices = m_libCec->GetActiveDevices();
 
-  bool StartBootloader(void)
-  {
-    return m_libCec->StartBootloader();
-  }
+      for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
+        if (activeDevices[iPtr])
+          retVal->Set((CecLogicalAddress)iPtr);
 
-  int GetMinLibVersion(void)
-  {
-    return m_libCec->GetMinLibVersion();
-  }
+      return retVal;
+    }
 
-  int GetLibVersionMajor(void)
-  {
-    return m_libCec->GetLibVersionMajor();
-  }
+    /// <summary>
+    /// Check whether a device is active on the bus.
+    /// </summary>
+    /// <param name="logicalAddress">The address to check.</param>
+    /// <returns>True when active, false otherwise.</returns>
+    bool IsActiveDevice(CecLogicalAddress logicalAddress)
+    {
+      return m_libCec->IsActiveDevice((cec_logical_address)logicalAddress);
+    }
 
-  int GetLibVersionMinor(void)
-  {
-    return m_libCec->GetLibVersionMinor();
-  }
+    /// <summary>
+    /// Check whether a device of the given type is active on the bus.
+    /// </summary>
+    /// <param name="type">The type to check.</param>
+    /// <returns>True when active, false otherwise.</returns>
+    bool IsActiveDeviceType(CecDeviceType type)
+    {
+      return m_libCec->IsActiveDeviceType((cec_device_type)type);
+    }
 
-  CecLogMessage ^ GetNextLogMessage(void)
-  {
-    cec_log_message msg;
-    if (m_libCec->GetNextLogMessage(&msg))
+    /// <summary>
+    /// Changes the active HDMI port.
+    /// </summary>
+    /// <param name="address">The device to which this libCEC is connected.</param>
+    /// <param name="port">The new port number.</param>
+    /// <returns>True when changed, false otherwise.</returns>
+    bool SetHDMIPort(CecLogicalAddress address, uint8_t port)
     {
-      return gcnew CecLogMessage(gcnew String(msg.message), (CecLogLevel)msg.level, msg.time);
+      return m_libCec->SetHDMIPort((cec_logical_address)address, port);
     }
 
-    return gcnew CecLogMessage();
-  }
+    /// <summary>
+    /// Sends a volume up keypress to an audiosystem if it's present.
+    /// </summary>
+    /// <param name="sendRelease">Send a key release after the keypress.</param>
+    /// <returns>The new audio status.</returns>
+    uint8_t VolumeUp(bool sendRelease)
+    {
+      return m_libCec->VolumeUp(sendRelease);
+    }
 
-  CecKeypress ^ GetNextKeypress(void)
-  {
-    cec_keypress key;
-    if (m_libCec->GetNextKeypress(&key))
+    /// <summary>
+    /// Sends a volume down keypress to an audiosystem if it's present.
+    /// </summary>
+    /// <param name="sendRelease">Send a key release after the keypress.</param>
+    /// <returns>The new audio status.</returns>
+    uint8_t VolumeDown(bool sendRelease)
     {
-      return gcnew CecKeypress(key.keycode, key.duration);
+      return m_libCec->VolumeDown(sendRelease);
     }
 
-    return gcnew CecKeypress();
-  }
+    /// <summary>
+    /// Sends a mute keypress to an audiosystem if it's present.
+    /// </summary>
+    /// <param name="sendRelease">Send a key release after the keypress.</param>
+    /// <returns>The new audio status.</returns>
+    uint8_t MuteAudio(bool sendRelease)
+    {
+      return m_libCec->MuteAudio(sendRelease);
+    }
 
-  CecCommand ^ GetNextCommand(void)
-  {
-    cec_command command;
-    if (m_libCec->GetNextCommand(&command))
+    /// <summary>
+    /// Send a keypress to a device on the CEC bus.
+    /// </summary>
+    /// <param name="destination">The logical address of the device to send the message to.</param>
+    /// <param name="key">The key to send.</param>
+    /// <param name="wait">True to wait for a response, false otherwise.</param>
+    /// <returns>True when the keypress was acked, false otherwise.</returns>
+    bool SendKeypress(CecLogicalAddress destination, CecUserControlCode key, bool wait)
     {
-      CecCommand ^ retVal = 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++)
-        retVal->Parameters->PushBack(command.parameters[iPtr]);
-      return retVal;
+      return m_libCec->SendKeypress((cec_logical_address)destination, (cec_user_control_code)key, wait);
     }
 
-    return gcnew CecCommand();
-  }
+    /// <summary>
+    /// Send a key release to a device on the CEC bus.
+    /// </summary>
+    /// <param name="destination">The logical address of the device to send the message to.</param>
+    /// <param name="wait">True to wait for a response, false otherwise.</param>
+    /// <returns>True when the key release was acked, false otherwise.</returns>
+    bool SendKeyRelease(CecLogicalAddress destination, bool wait)
+    {
+      return m_libCec->SendKeyRelease((cec_logical_address)destination, wait);
+    }
 
-  bool Transmit(CecCommand ^ command)
-  {
-    cec_command ccommand;
-    cec_command::Format(ccommand, (cec_logical_address)command->Initiator, (cec_logical_address)command->Destination, (cec_opcode)command->Opcode);
-    ccommand.transmit_timeout = command->TransmitTimeout;
-    ccommand.eom              = command->Eom;
-    ccommand.ack              = command->Ack;
-    for (unsigned int iPtr = 0; iPtr < command->Parameters->Size; iPtr++)
-      ccommand.parameters.PushBack(command->Parameters->Data[iPtr]);
-
-    return m_libCec->Transmit(ccommand);
-  }
-
-  bool SetLogicalAddress(CecLogicalAddress logicalAddress)
-  {
-    return m_libCec->SetLogicalAddress((cec_logical_address) logicalAddress);
-  }
+    /// <summary>
+    /// Get the OSD name of a device on the CEC bus.
+    /// </summary>
+    /// <param name="logicalAddress">The logical address of the device to get the OSD name for.</param>
+    /// <returns>The OSD name.</returns>
+    String ^ GetDeviceOSDName(CecLogicalAddress logicalAddress)
+    {
+      cec_osd_name osd = m_libCec->GetDeviceOSDName((cec_logical_address) logicalAddress);
+      // we need to terminate with \0, and we only got 14 chars in osd.name
+      char strOsdName[15];
+      memset(strOsdName, 0, sizeof(strOsdName));
+      memcpy(strOsdName, osd.name, sizeof(osd.name));
+      return gcnew String(strOsdName);
+    }
 
-  bool SetPhysicalAddress(int16_t physicalAddress)
-  {
-    return m_libCec->SetPhysicalAddress(physicalAddress);
-  }
+    /// <summary>
+    /// Get the logical address of the device that is currently the active source on the CEC bus.
+    /// </summary>
+    /// <returns>The active source or CECDEVICE_UNKNOWN when unknown.</returns>
+    CecLogicalAddress GetActiveSource()
+    {
+      return (CecLogicalAddress)m_libCec->GetActiveSource();
+    }
 
-  bool PowerOnDevices(CecLogicalAddress logicalAddress)
-  {
-    return m_libCec->PowerOnDevices((cec_logical_address) logicalAddress);
-  }
+    /// <summary>
+    /// Check whether a device is currently the active source on the CEC bus.
+    /// </summary>
+    /// <param name="logicalAddress">The logical address of the device to check.</param>
+    /// <returns>True when it is the active source, false otherwise.</returns>
+    bool IsActiveSource(CecLogicalAddress logicalAddress)
+    {
+      return m_libCec->IsActiveSource((cec_logical_address)logicalAddress);
+    }
 
-  bool StandbyDevices(CecLogicalAddress logicalAddress)
-  {
-    return m_libCec->StandbyDevices((cec_logical_address) logicalAddress);
-  }
+    /// <summary>
+    /// Get the physical address of the device with the given logical address.
+    /// </summary>
+    /// <param name="address">The logical address of the device to get the physical address for.</param>
+    /// <returns>The physical address or 0 if it wasn't found.</returns>
+    uint16_t GetDevicePhysicalAddress(CecLogicalAddress address)
+    {
+      return m_libCec->GetDevicePhysicalAddress((cec_logical_address)address);
+    }
 
-  bool PollDevice(CecLogicalAddress logicalAddress)
-  {
-    return m_libCec->PollDevice((cec_logical_address) logicalAddress);
-  }
+    /// <summary>
+    /// Sets the stream path to the device on the given logical address.
+    /// </summary>
+    /// <param name="address">The address to activate.</param>
+    /// <returns>True when the command was sent, false otherwise.</returns>
+    bool SetStreamPath(CecLogicalAddress address)
+    {
+      return m_libCec->SetStreamPath((cec_logical_address)address);
+    }
 
-  bool SetActiveSource(CecDeviceType type)
-  {
-    return m_libCec->SetActiveSource((cec_device_type) type);
-  }
+    /// <summary>
+    /// Sets the stream path to the device on the given physical address.
+    /// </summary>
+    /// <param name="physicalAddress">The address to activate.</param>
+    /// <returns>True when the command was sent, false otherwise.</returns>
+    bool SetStreamPath(uint16_t physicalAddress)
+    {
+      return m_libCec->SetStreamPath(physicalAddress);
+    }
 
-  bool SetDeckControlMode(CecDeckControlMode mode, bool sendUpdate)
-  {
-    return m_libCec->SetDeckControlMode((cec_deck_control_mode) mode, sendUpdate);
-  }
+    /// <summary>
+    /// Get the list of logical addresses that libCEC is controlling
+    /// </summary>
+    /// <returns>The list of logical addresses that libCEC is controlling</returns>
+    CecLogicalAddresses ^GetLogicalAddresses(void)
+    {
+      CecLogicalAddresses ^addr = gcnew CecLogicalAddresses();
+      cec_logical_addresses libAddr = m_libCec->GetLogicalAddresses();
+      for (unsigned int iPtr = 0; iPtr < 16; iPtr++)
+        addr->Addresses[iPtr] = (CecLogicalAddress)libAddr.addresses[iPtr];
+      addr->Primary = (CecLogicalAddress)libAddr.primary;
+      return addr;
+    }
 
-  bool SetDeckInfo(CecDeckInfo info, bool sendUpdate)
-  {
-    return m_libCec->SetDeckInfo((cec_deck_info) info, sendUpdate);
-  }
+    /// <summary>
+    /// Get libCEC's current configuration.
+    /// </summary>
+    /// <param name="configuration">The configuration.</param>
+    /// <returns>True when the configuration was updated, false otherwise.</returns>
+    bool GetCurrentConfiguration(LibCECConfiguration ^configuration)
+    {
+      libcec_configuration config;
+      config.Clear();
+
+      if (m_libCec->GetCurrentConfiguration(&config))
+      {
+        configuration->Update(config);
+        return true;
+      }
+      return false;
+    }
 
-  bool SetInactiveView(void)
-  {
-    return m_libCec->SetInactiveView();
-  }
+    /// <summary>
+    /// Check whether the CEC adapter can persist a configuration.
+    /// </summary>
+    /// <returns>True when this CEC adapter can persist the user configuration, false otherwise.</returns>
+    bool CanPersistConfiguration(void)
+    {
+      return m_libCec->CanPersistConfiguration();
+    }
 
-  bool SetMenuState(CecMenuState state, bool sendUpdate)
-  {
-    return m_libCec->SetMenuState((cec_menu_state) state, sendUpdate);
-  }
+    /// <summary>
+    /// Persist the given configuration in adapter (if supported)
+    /// </summary>
+    /// <param name="configuration">The configuration to store.</param>
+    /// <returns>True when the configuration was persisted, false otherwise.</returns>
+    bool PersistConfiguration(LibCECConfiguration ^configuration)
+    {
+      marshal_context ^ context = gcnew marshal_context();
+      libcec_configuration config;
+      ConvertConfiguration(context, configuration, config);
 
-  bool SetOSDString(CecLogicalAddress logicalAddress, CecDisplayControl duration, String ^ message)
-  {
-    marshal_context ^ context = gcnew marshal_context();
-    const char* strMessageC = context->marshal_as<const char*>(message);
+      bool bReturn = m_libCec->PersistConfiguration(&config);
 
-    bool bReturn = m_libCec->SetOSDString((cec_logical_address) logicalAddress, (cec_display_control) duration, strMessageC);
+      delete context;
+      return bReturn;
+    }
 
-    delete context;
-    return bReturn;
-  }
+    /// <summary>
+    /// Change libCEC's configuration.
+    /// </summary>
+    /// <param name="configuration">The new configuration.</param>
+    /// <returns>True when the configuration was changed successfully, false otherwise.</returns>
+    bool SetConfiguration(LibCECConfiguration ^configuration)
+    {
+      marshal_context ^ context = gcnew marshal_context();
+      libcec_configuration config;
+      ConvertConfiguration(context, configuration, config);
 
-  bool SwitchMonitoring(bool enable)
-  {
-    return m_libCec->SwitchMonitoring(enable);
-  }
+      bool bReturn = m_libCec->SetConfiguration(&config);
 
-  CecVersion GetDeviceCecVersion(CecLogicalAddress logicalAddress)
-  {
-    return (CecVersion) m_libCec->GetDeviceCecVersion((cec_logical_address) logicalAddress);
-  }
+      delete context;
+      return bReturn;
+    }
 
-  String ^ GetDeviceMenuLanguage(CecLogicalAddress logicalAddress)
-  {
-    cec_menu_language lang;
-    if (m_libCec->GetDeviceMenuLanguage((cec_logical_address) logicalAddress, &lang))
+    /// <summary>
+    /// Check whether libCEC is the active source on the bus.
+    /// </summary>
+    /// <returns>True when libCEC is the active source on the bus, false otherwise.</returns>
+    bool IsLibCECActiveSource()
     {
-      return gcnew String(lang.language);
+      return m_libCec->IsLibCECActiveSource();
     }
 
-    return gcnew String("");
-  }
-
-  CecVendorId GetDeviceVendorId(CecLogicalAddress logicalAddress)
-  {
-    return (CecVendorId)m_libCec->GetDeviceVendorId((cec_logical_address) logicalAddress);
-  }
-
-  CecPowerStatus GetDevicePowerStatus(CecLogicalAddress logicalAddress)
-  {
-    return (CecPowerStatus) m_libCec->GetDevicePowerStatus((cec_logical_address) logicalAddress);
-  }
-
-  CecLogicalAddresses ^ GetActiveDevices(void)
-  {
-    CecLogicalAddresses ^ retVal = gcnew CecLogicalAddresses();
-    unsigned int iDevices = 0;
-
-    cec_logical_addresses activeDevices = m_libCec->GetActiveDevices();
-
-    for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
-      if (activeDevices[iPtr])
-        retVal->Addresses[iDevices++] = (CecLogicalAddress)iPtr;
+    /// <summary>
+    /// Get information about the given CEC adapter.
+    /// </summary>
+    /// <param name="port">The COM port to which the device is connected</param>
+    /// <param name="configuration">The device configuration</param>
+    /// <param name="timeoutMs">The timeout in milliseconds</param>
+    /// <returns>True when the device was found, false otherwise</returns>
+    bool GetDeviceInformation(String ^ port, LibCECConfiguration ^configuration, uint32_t timeoutMs)
+    {
+      bool bReturn(false);
+      marshal_context ^ context = gcnew marshal_context();
 
-    return retVal;
-  }
+      libcec_configuration config;
+      config.Clear();
 
-  bool IsActiveDevice(CecLogicalAddress logicalAddress)
-  {
-    return m_libCec->IsActiveDevice((cec_logical_address)logicalAddress);
-  }
+      const char* strPortC = port->Length > 0 ? context->marshal_as<const char*>(port) : NULL;
 
-  bool IsActiveDeviceType(CecDeviceType type)
-  {
-    return m_libCec->IsActiveDeviceType((cec_device_type)type);
-  }
+      if (m_libCec->GetDeviceInformation(strPortC, &config, timeoutMs))
+      {
+        configuration->Update(config);
+        bReturn = true;
+      }
 
-  bool SetHDMIPort(CecLogicalAddress address, uint8_t port)
-  {
-    return m_libCec->SetHDMIPort((cec_logical_address)address, port);
-  }
+      delete context;
+      return bReturn;
+    }
 
-  uint8_t VolumeUp(bool wait)
-  {
-    return m_libCec->VolumeUp(wait);
-  }
+    String ^ ToString(CecLogicalAddress iAddress)
+    {
+      const char *retVal = m_libCec->ToString((cec_logical_address)iAddress);
+      return gcnew String(retVal);
+    }
 
-  uint8_t VolumeDown(bool wait)
-  {
-    return m_libCec->VolumeDown(wait);
-  }
+    String ^ ToString(CecVendorId iVendorId)
+    {
+      const char *retVal = m_libCec->ToString((cec_vendor_id)iVendorId);
+      return gcnew String(retVal);
+    }
 
-  uint8_t MuteAudio(bool wait)
-  {
-    return m_libCec->MuteAudio(wait);
-  }
+    String ^ ToString(CecVersion iVersion)
+    {
+      const char *retVal = m_libCec->ToString((cec_version)iVersion);
+      return gcnew String(retVal);
+    }
 
-  bool SendKeypress(CecLogicalAddress destination, CecUserControlCode key, bool wait)
-  {
-    return m_libCec->SendKeypress((cec_logical_address)destination, (cec_user_control_code)key, wait);
-  }
+    String ^ ToString(CecPowerStatus iState)
+    {
+      const char *retVal = m_libCec->ToString((cec_power_status)iState);
+      return gcnew String(retVal);
+    }
 
-  bool SendKeyRelease(CecLogicalAddress destination, bool wait)
-  {
-    return m_libCec->SendKeyRelease((cec_logical_address)destination, wait);
-  }
+    String ^ ToString(CecMenuState iState)
+    {
+      const char *retVal = m_libCec->ToString((cec_menu_state)iState);
+      return gcnew String(retVal);
+    }
 
-  String ^ GetDeviceOSDName(CecLogicalAddress logicalAddress)
-  {
-    cec_osd_name osd = m_libCec->GetDeviceOSDName((cec_logical_address) logicalAddress);
-    return gcnew String(osd.name);
-  }
+    String ^ ToString(CecDeckControlMode iMode)
+    {
+      const char *retVal = m_libCec->ToString((cec_deck_control_mode)iMode);
+      return gcnew String(retVal);
+    }
 
-  CecLogicalAddress GetActiveSource()
-  {
-    return (CecLogicalAddress)m_libCec->GetActiveSource();
-  }
+    String ^ ToString(CecDeckInfo status)
+    {
+      const char *retVal = m_libCec->ToString((cec_deck_info)status);
+      return gcnew String(retVal);
+    }
 
-  bool IsActiveSource(CecLogicalAddress logicalAddress)
-  {
-    return m_libCec->IsActiveSource((cec_logical_address)logicalAddress);
-  }
+    String ^ ToString(CecOpcode opcode)
+    {
+      const char *retVal = m_libCec->ToString((cec_opcode)opcode);
+      return gcnew String(retVal);
+    }
 
-  uint16_t GetDevicePhysicalAddress(CecLogicalAddress iAddress)
-  {
-    return m_libCec->GetDevicePhysicalAddress((cec_logical_address)iAddress);
-  }
+    String ^ ToString(CecSystemAudioStatus mode)
+    {
+      const char *retVal = m_libCec->ToString((cec_system_audio_status)mode);
+      return gcnew String(retVal);
+    }
 
-  String ^ ToString(CecLogicalAddress iAddress)
-  {
-    const char *retVal = m_libCec->ToString((cec_logical_address)iAddress);
-    return gcnew String(retVal);
-  }
+    String ^ ToString(CecAudioStatus status)
+    {
+      const char *retVal = m_libCec->ToString((cec_audio_status)status);
+      return gcnew String(retVal);
+    }
 
-  String ^ ToString(CecVendorId iVendorId)
-  {
-    const char *retVal = m_libCec->ToString((cec_vendor_id)iVendorId);
-    return gcnew String(retVal);
-  }
-  
-  String ^ ToString(CecVersion iVersion)
-  {
-    const char *retVal = m_libCec->ToString((cec_version)iVersion);
-    return gcnew String(retVal);
-  }
-  
-  String ^ ToString(CecPowerStatus iState)
-  {
-    const char *retVal = m_libCec->ToString((cec_power_status)iState);
-    return gcnew String(retVal);
-  }
+    String ^ ToString(CecClientVersion version)
+    {
+      const char *retVal = m_libCec->ToString((cec_client_version)version);
+      return gcnew String(retVal);
+    }
 
-  String ^ ToString(CecMenuState iState)
-  {
-    const char *retVal = m_libCec->ToString((cec_menu_state)iState);
-    return gcnew String(retVal);
-  }
+    String ^ ToString(CecServerVersion version)
+    {
+      const char *retVal = m_libCec->ToString((cec_server_version)version);
+      return gcnew String(retVal);
+    }
 
-  String ^ ToString(CecDeckControlMode iMode)
-  {
-    const char *retVal = m_libCec->ToString((cec_deck_control_mode)iMode);
-    return gcnew String(retVal);
-  }
+    /// <summary>
+    /// Get a string with information about how libCEC was compiled.
+    /// </summary>
+    /// <returns>A string with information about how libCEC was compiled.</returns>
+    String ^ GetLibInfo()
+    {
+      const char *retVal = m_libCec->GetLibInfo();
+      return gcnew String(retVal);
+    }
 
-  String ^ ToString(CecDeckInfo status)
-  {
-    const char *retVal = m_libCec->ToString((cec_deck_info)status);
-    return gcnew String(retVal);
-  }
+    /// <summary>
+    /// Calling this method will initialise the host on which libCEC is running.
+    /// On the RPi, it calls bcm_host_init(), which may only be called once per process, and is called by any process using
+    /// the video api on that system. So only call this method if libCEC is used in an application that
+    /// does not already initialise the video api.
+    /// </summary>
+    /// <remarks>Should be called as first call to libCEC, directly after CECInitialise() and before using Open()</remarks>
+    void InitVideoStandalone()
+    {
+      m_libCec->InitVideoStandalone();
+    }
 
-  String ^ ToString(CecOpcode opcode)
-  {
-    const char *retVal = m_libCec->ToString((cec_opcode)opcode);
-    return gcnew String(retVal);
-  }
+    /// <summary>
+    /// Get the (virtual) USB vendor id
+    /// </summary>
+    /// <returns>The (virtual) USB vendor id</returns>
+    uint16_t GetAdapterVendorId()
+    {
+      return m_libCec->GetAdapterVendorId();
+    }
 
-  String ^ ToString(CecSystemAudioStatus mode)
-  {
-    const char *retVal = m_libCec->ToString((cec_system_audio_status)mode);
-    return gcnew String(retVal);
-  }
+    /// <summary>
+    /// Get the (virtual) USB product id
+    /// </summary>
+    /// <returns>The (virtual) USB product id</returns>
+    uint16_t GetAdapterProductId()
+    {
+      return m_libCec->GetAdapterProductId();
+    }
 
-  String ^ ToString(CecAudioStatus status)
-  {
-    const char *retVal = m_libCec->ToString((cec_audio_status)status);
-    return gcnew String(retVal);
-  }
+  private:
+    !LibCecSharp(void)
+    {
+      Close();
+      m_libCec = NULL;
+    }
 
-private:
-  void DestroyDelegates()
-  {
-    m_logMessageGCHandle.Free();
-    m_keypressGCHandle.Free();
-    m_commandGCHandle.Free();
-  }
+    bool InitialiseLibCec(LibCECConfiguration ^config)
+    {
+      marshal_context ^ context = gcnew marshal_context();
+      libcec_configuration libCecConfig;
+      ConvertConfiguration(context, config, libCecConfig);
 
-  // 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;
-  }
+      m_libCec = (ICECAdapter *) CECInitialise(&libCecConfig);
+      config->Update(libCecConfig);
 
-  int CecKeyPressManaged(const cec_keypress &key)
-  {
-    int iReturn(0);
-    if (m_bHasCallbacks)
-      iReturn = m_callbacks->ReceiveKeypress(gcnew CecKeypress(key.keycode, key.duration));
-    return iReturn;
-  }
+      delete context;
+      return m_libCec != NULL;
+    }
 
-  int CecCommandManaged(const cec_command &command)
-  {
-    int iReturn(0);
-    if (m_bHasCallbacks)
+    void ConvertConfiguration(marshal_context ^context, LibCECConfiguration ^netConfig, CEC::libcec_configuration &config)
     {
-      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);
+      config.Clear();
+
+      const char *strDeviceName = context->marshal_as<const char*>(netConfig->DeviceName);
+      memcpy_s(config.strDeviceName, 13, strDeviceName, 13);
+      for (unsigned int iPtr = 0; iPtr < 5; iPtr++)
+        config.deviceTypes.types[iPtr] = (cec_device_type)netConfig->DeviceTypes->Types[iPtr];
+
+      config.bAutodetectAddress   = netConfig->AutodetectAddress ? 1 : 0;
+      config.iPhysicalAddress     = netConfig->PhysicalAddress;
+      config.baseDevice           = (cec_logical_address)netConfig->BaseDevice;
+      config.iHDMIPort            = netConfig->HDMIPort;
+      config.clientVersion        = (cec_client_version)netConfig->ClientVersion;
+      config.bGetSettingsFromROM  = netConfig->GetSettingsFromROM ? 1 : 0;
+      config.bActivateSource      = netConfig->ActivateSource ? 1 : 0;
+      config.tvVendor             = (cec_vendor_id)netConfig->TvVendor;
+      config.wakeDevices.Clear();
+      for (int iPtr = 0; iPtr < 16; iPtr++)
+      {
+        if (netConfig->WakeDevices->IsSet((CecLogicalAddress)iPtr))
+          config.wakeDevices.Set((cec_logical_address)iPtr);
+      }
+      config.powerOffDevices.Clear();
+      for (int iPtr = 0; iPtr < 16; iPtr++)
+      {
+        if (netConfig->PowerOffDevices->IsSet((CecLogicalAddress)iPtr))
+          config.powerOffDevices.Set((cec_logical_address)iPtr);
+      }
+      config.bPowerOffScreensaver = netConfig->PowerOffScreensaver ? 1 : 0;
+      config.bPowerOffOnStandby   = netConfig->PowerOffOnStandby ? 1 : 0;
+
+      if (netConfig->ServerVersion >= CecServerVersion::Version1_5_1)
+        config.bSendInactiveSource  = netConfig->SendInactiveSource ? 1 : 0;
+
+      if (netConfig->ServerVersion >= CecServerVersion::Version1_6_0)
+      {
+        config.bPowerOffDevicesOnStandby  = netConfig->PowerOffDevicesOnStandby ? 1 : 0;
+        config.bShutdownOnStandby         = netConfig->ShutdownOnStandby ? 1 : 0;
+      }
+
+      if (netConfig->ServerVersion >= CecServerVersion::Version1_6_2)
+      {
+        const char *strDeviceLanguage = context->marshal_as<const char*>(netConfig->DeviceLanguage);
+        memcpy_s(config.strDeviceLanguage, 3, strDeviceLanguage, 3);
+      }
+
+      if (netConfig->ServerVersion >= CecServerVersion::Version1_6_3)
+        config.bMonitorOnly = netConfig->MonitorOnlyClient ? 1 : 0;
+
+      if (netConfig->ServerVersion >= CecServerVersion::Version1_8_0)
+        config.cecVersion = (cec_version)netConfig->CECVersion;
+
+      if (netConfig->ServerVersion >= CecServerVersion::Version2_1_0)
+        config.bPowerOnScreensaver  = netConfig->PowerOnScreensaver ? 1 : 0;
+
+      config.callbacks = &g_cecCallbacks;
     }
-    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;
-};
+    ICECAdapter *        m_libCec;
+    CecCallbackMethods ^ m_callbacks;
+  };
+}