From 5469da2318ce2816ab80b34e6f4eaeb8df50e4a2 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Tue, 14 Mar 2017 11:11:08 +0100 Subject: [PATCH 01/93] added: log a warning when we detect that RPi's CEC service is used by something else, blocking libCEC. issue #191 --- src/libcec/adapter/RPi/RPiCECAdapterCommunication.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.cpp b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.cpp index 64fb084c..14b5e852 100644 --- a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.cpp +++ b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.cpp @@ -421,7 +421,9 @@ bool CRPiCECAdapterCommunication::RegisterLogicalAddress(const cec_logical_addre int iRetval = vc_cec_set_logical_address((CEC_AllDevices_T)address, (CEC_DEVICE_TYPE_T)CCECTypeUtils::GetType(address), CEC_VENDOR_ID_BROADCOM); if (iRetval != VCHIQ_SUCCESS) { - LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vc_cec_set_logical_address(%X) returned %s (%d)", __FUNCTION__, address, ToString((VC_CEC_ERROR_T)iRetval), iRetval); + LIB_CEC->AddLog(CEC_LOG_WARNING, "%s - vc_cec_set_logical_address(%X) returned %s (%d)", __FUNCTION__, address, ToString((VC_CEC_ERROR_T)iRetval), iRetval); + if (iRetval == VC_CEC_ERROR_INVALID_ARGUMENT) + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - CEC is being used by another application. Run \"tvservice --off\" and try again.", __FUNCTION__); UnregisterLogicalAddress(); } else if (m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged, iTimeoutMs)) From 0cf3f0fe0bfde8d7de1faf569f2ea91ba1e6fcc4 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Tue, 14 Mar 2017 11:21:52 +0100 Subject: [PATCH 02/93] changed: const IAdapterCommunication::GetLogicalAddresses(), making the mutex mutable for now without changing the platform lib. closes #259 --- src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp | 2 +- src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h | 4 ++-- src/libcec/adapter/AdapterCommunication.h | 3 ++- src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.cpp | 3 ++- src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h | 4 ++-- .../adapter/Pulse-Eight/USBCECAdapterCommunication.cpp | 4 ++-- src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h | 6 +++--- src/libcec/adapter/RPi/RPiCECAdapterCommunication.cpp | 5 +++-- src/libcec/adapter/RPi/RPiCECAdapterCommunication.h | 6 +++--- .../adapter/TDA995x/TDA995xCECAdapterCommunication.cpp | 2 +- src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h | 4 ++-- 11 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp index a88d9bab..538938e0 100644 --- a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp +++ b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp @@ -210,7 +210,7 @@ uint16_t CAOCECAdapterCommunication::GetPhysicalAddress(void) return (uint16_t)phys_addr; } -cec_logical_addresses CAOCECAdapterCommunication::GetLogicalAddresses(void) +cec_logical_addresses CAOCECAdapterCommunication::GetLogicalAddresses(void) const { CLockObject lock(m_mutex); return m_logicalAddresses; diff --git a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h index 348f7b64..65964025 100644 --- a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h +++ b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h @@ -66,7 +66,7 @@ namespace CEC bool SetLineTimeout(uint8_t UNUSED(iTimeout)) { return true; } bool StartBootloader(void) { return false; } bool SetLogicalAddresses(const cec_logical_addresses &addresses); - cec_logical_addresses GetLogicalAddresses(void); + cec_logical_addresses GetLogicalAddresses(void) const; bool PingAdapter(void) { return IsInitialised(); } uint16_t GetFirmwareVersion(void); uint32_t GetFirmwareBuildDate(void) { return 0; } @@ -97,7 +97,7 @@ namespace CEC bool m_bLogicalAddressChanged; cec_logical_addresses m_logicalAddresses; - P8PLATFORM::CMutex m_mutex; + mutable P8PLATFORM::CMutex m_mutex; int m_fd; }; }; diff --git a/src/libcec/adapter/AdapterCommunication.h b/src/libcec/adapter/AdapterCommunication.h index 6b632f0b..74c80751 100644 --- a/src/libcec/adapter/AdapterCommunication.h +++ b/src/libcec/adapter/AdapterCommunication.h @@ -145,7 +145,8 @@ namespace CEC virtual bool StartBootloader(void) = 0; virtual bool SetLogicalAddresses(const cec_logical_addresses &addresses) = 0; - virtual cec_logical_addresses GetLogicalAddresses(void) = 0; + + virtual cec_logical_addresses GetLogicalAddresses(void) const = 0; /*! * @brief Check whether the CEC adapter responds diff --git a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.cpp b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.cpp index 285110ff..44cf4998 100644 --- a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.cpp +++ b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.cpp @@ -172,8 +172,9 @@ uint16_t CExynosCECAdapterCommunication::GetPhysicalAddress(void) } -cec_logical_addresses CExynosCECAdapterCommunication::GetLogicalAddresses(void) +cec_logical_addresses CExynosCECAdapterCommunication::GetLogicalAddresses(void) const { + CLockObject lock(m_mutex); return m_logicalAddresses; } diff --git a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h index 24276c06..9292fdcb 100644 --- a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h +++ b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h @@ -64,7 +64,7 @@ namespace CEC bool SetLineTimeout(uint8_t UNUSED(iTimeout)) { return true; } bool StartBootloader(void) { return false; } bool SetLogicalAddresses(const cec_logical_addresses &addresses); - cec_logical_addresses GetLogicalAddresses(void); + cec_logical_addresses GetLogicalAddresses(void) const; bool PingAdapter(void) { return IsInitialised(); } uint16_t GetFirmwareVersion(void); uint32_t GetFirmwareBuildDate(void) { return 0; } @@ -96,7 +96,7 @@ namespace CEC bool m_bLogicalAddressChanged; cec_logical_addresses m_logicalAddresses; - P8PLATFORM::CMutex m_mutex; + mutable P8PLATFORM::CMutex m_mutex; int m_fd; }; }; diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp index fecc0446..ee01c889 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp @@ -531,7 +531,7 @@ void CUSBCECAdapterCommunication::SetInitialised(bool bSetTo /* = true */) m_bInitialised = bSetTo; } -bool CUSBCECAdapterCommunication::IsInitialised(void) +bool CUSBCECAdapterCommunication::IsInitialised(void) const { CLockObject lock(m_mutex); return m_bInitialised; @@ -566,7 +566,7 @@ bool CUSBCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresse return false; } -cec_logical_addresses CUSBCECAdapterCommunication::GetLogicalAddresses(void) +cec_logical_addresses CUSBCECAdapterCommunication::GetLogicalAddresses(void) const { cec_logical_addresses addresses; CLockObject lock(m_mutex); diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h index 63a73a68..d5096cc5 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h @@ -77,7 +77,7 @@ namespace CEC bool StartBootloader(void); bool SetLogicalAddresses(const cec_logical_addresses &addresses); - cec_logical_addresses GetLogicalAddresses(void); + cec_logical_addresses GetLogicalAddresses(void) const; bool PingAdapter(void); uint16_t GetFirmwareVersion(void); uint32_t GetFirmwareBuildDate(void); @@ -131,7 +131,7 @@ namespace CEC /*! * @return True when initialised, false otherwise. */ - bool IsInitialised(void); + bool IsInitialised(void) const; /*! * @brief Pings the adapter, checks the firmware version and sets controlled mode. @@ -174,7 +174,7 @@ namespace CEC void ResetMessageQueue(void); P8PLATFORM::ISocket * m_port; /**< the com port connection */ - P8PLATFORM::CMutex m_mutex; /**< mutex for changes in this class */ + mutable P8PLATFORM::CMutex m_mutex; /**< mutex for changes in this class */ uint8_t m_iLineTimeout; /**< the current line timeout on the CEC line */ cec_logical_address m_lastPollDestination; /**< the destination of the last poll message that was received */ bool m_bInitialised; /**< true when the connection is initialised, false otherwise */ diff --git a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.cpp b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.cpp index 14b5e852..962d5dd9 100644 --- a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.cpp +++ b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.cpp @@ -382,7 +382,7 @@ uint16_t CRPiCECAdapterCommunication::GetFirmwareVersion(void) return VC_CECSERVICE_VER; } -cec_logical_address CRPiCECAdapterCommunication::GetLogicalAddress(void) +cec_logical_address CRPiCECAdapterCommunication::GetLogicalAddress(void) const { CLockObject lock(m_mutex); @@ -433,8 +433,9 @@ bool CRPiCECAdapterCommunication::RegisterLogicalAddress(const cec_logical_addre return false; } -cec_logical_addresses CRPiCECAdapterCommunication::GetLogicalAddresses(void) +cec_logical_addresses CRPiCECAdapterCommunication::GetLogicalAddresses(void) const { + CLockObject lock(m_mutex); cec_logical_addresses addresses; addresses.Clear(); if (m_bLogicalAddressRegistered) addresses.primary = GetLogicalAddress(); diff --git a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h index 19bb0e54..411f224c 100644 --- a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h +++ b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h @@ -71,7 +71,7 @@ namespace CEC bool SetLineTimeout(uint8_t UNUSED(iTimeout)) { return true; }; bool StartBootloader(void) { return false; }; bool SetLogicalAddresses(const cec_logical_addresses &addresses); - cec_logical_addresses GetLogicalAddresses(void); + cec_logical_addresses GetLogicalAddresses(void) const; bool PingAdapter(void) { return m_bInitialised; }; uint16_t GetFirmwareVersion(void); uint32_t GetFirmwareBuildDate(void) { return 0; }; @@ -96,7 +96,7 @@ namespace CEC static void InitHost(void); private: - cec_logical_address GetLogicalAddress(void); + cec_logical_address GetLogicalAddress(void) const; bool UnregisterLogicalAddress(void); bool RegisterLogicalAddress(const cec_logical_address address, uint32_t iTimeoutMs = CEC_DEFAULT_CONNECT_TIMEOUT); void SetDisableCallback(const bool disable); @@ -108,7 +108,7 @@ namespace CEC bool m_bLogicalAddressChanged; P8PLATFORM::CCondition m_logicalAddressCondition; - P8PLATFORM::CMutex m_mutex; + mutable P8PLATFORM::CMutex m_mutex; VCHI_INSTANCE_T m_vchi_instance; VCHI_CONNECTION_T * m_vchi_connection; cec_logical_address m_previousLogicalAddress; diff --git a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp index 734e3bb7..377a5743 100644 --- a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp +++ b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp @@ -248,7 +248,7 @@ uint16_t CTDA995xCECAdapterCommunication::GetPhysicalAddress(void) } -cec_logical_addresses CTDA995xCECAdapterCommunication::GetLogicalAddresses(void) +cec_logical_addresses CTDA995xCECAdapterCommunication::GetLogicalAddresses(void) const { CLockObject lock(m_mutex); diff --git a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h index 47dde4be..326b3cf6 100644 --- a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h +++ b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h @@ -75,7 +75,7 @@ namespace CEC bool SetLineTimeout(uint8_t UNUSED(iTimeout)) { return true; } bool StartBootloader(void) { return false; } bool SetLogicalAddresses(const cec_logical_addresses &addresses); - cec_logical_addresses GetLogicalAddresses(void); + cec_logical_addresses GetLogicalAddresses(void) const; bool PingAdapter(void) { return IsInitialised(); } uint16_t GetFirmwareVersion(void); uint32_t GetFirmwareBuildDate(void) { return 0; } @@ -107,7 +107,7 @@ namespace CEC bool m_bLogicalAddressChanged; cec_logical_addresses m_logicalAddresses; - P8PLATFORM::CMutex m_mutex; + mutable P8PLATFORM::CMutex m_mutex; P8PLATFORM::CCDevSocket *m_dev; /**< the device connection */ P8PLATFORM::CMutex m_messageMutex; From 50f1179de3d3eb78b3cc85e593f137dcc5378c45 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Tue, 14 Mar 2017 15:08:57 +0100 Subject: [PATCH 03/93] fixed: cross compile includes --- src/LibCecSharp/CecSharpTypes.h | 2 +- src/cec-client/cec-client.cpp | 6 +++--- src/cec-client/curses/CursesControl.cpp | 2 +- src/cec-client/env.h.in | 2 +- src/cecc-client/env.h.in | 2 +- src/libcec/CECClient.h | 6 +++--- src/libcec/CECInputBuffer.h | 4 ++-- src/libcec/CECProcessor.cpp | 4 ++-- src/libcec/CECProcessor.h | 6 ++---- src/libcec/CECTypeUtils.h | 2 +- src/libcec/LibCEC.cpp | 4 ++-- src/libcec/LibCEC.h | 2 +- src/libcec/SwigHelper.h | 2 +- src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp | 2 +- src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h | 4 ++-- src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.cpp | 2 +- src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h | 4 ++-- src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp | 2 +- src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h | 2 +- .../adapter/Pulse-Eight/USBCECAdapterCommunication.cpp | 4 ++-- src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h | 2 +- src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp | 2 +- .../adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp | 2 +- src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.h | 6 +++--- src/libcec/adapter/RPi/RPiCECAdapterCommunication.h | 2 +- src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.cpp | 2 +- src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.h | 2 +- src/libcec/adapter/TDA995x/AdapterMessageQueue.h | 2 +- .../adapter/TDA995x/TDA995xCECAdapterCommunication.cpp | 4 ++-- src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h | 6 +++--- src/libcec/devices/CECBusDevice.cpp | 4 ++-- src/libcec/devices/CECBusDevice.h | 2 +- src/libcec/devices/CECPlaybackDevice.cpp | 1 - src/libcec/env.h.in | 2 +- src/libcec/implementations/AQCommandHandler.h | 2 +- src/libcec/implementations/CECCommandHandler.cpp | 2 +- src/libcec/implementations/CECCommandHandler.h | 2 +- src/libcec/implementations/PHCommandHandler.h | 2 +- src/libcec/implementations/RLCommandHandler.cpp | 2 +- src/libcec/implementations/SLCommandHandler.cpp | 2 +- src/libcec/platform/adl/adl-edid.cpp | 2 +- src/libcec/platform/drm/drm-edid.cpp | 2 +- src/libcec/platform/nvidia/nv-edid.cpp | 2 +- src/libcec/platform/posix/serialport.cpp | 2 +- src/libcec/platform/sockets/serialport.h | 4 ++-- src/libcec/platform/windows/serialport.cpp | 2 +- 46 files changed, 63 insertions(+), 66 deletions(-) diff --git a/src/LibCecSharp/CecSharpTypes.h b/src/LibCecSharp/CecSharpTypes.h index d73f4f27..434e1a8d 100644 --- a/src/LibCecSharp/CecSharpTypes.h +++ b/src/LibCecSharp/CecSharpTypes.h @@ -31,7 +31,7 @@ * http://www.pulse-eight.net/ */ -#include +#include "p8-platform/threads/mutex.h" #include #include #include "../../include/cec.h" diff --git a/src/cec-client/cec-client.cpp b/src/cec-client/cec-client.cpp index d654b308..a66c751f 100644 --- a/src/cec-client/cec-client.cpp +++ b/src/cec-client/cec-client.cpp @@ -42,9 +42,9 @@ #include #include #include -#include -#include -#include +#include "p8-platform/os.h" +#include "p8-platform/util/StringUtils.h" +#include "p8-platform/threads/threads.h" #if defined(HAVE_CURSES_API) #include "curses/CursesControl.h" #endif diff --git a/src/cec-client/curses/CursesControl.cpp b/src/cec-client/curses/CursesControl.cpp index c96ff4c0..d1c5e470 100644 --- a/src/cec-client/curses/CursesControl.cpp +++ b/src/cec-client/curses/CursesControl.cpp @@ -32,7 +32,7 @@ */ #include "CursesControl.h" -#include +#include "p8-platform/util/StringUtils.h" #include void CCursesControl::Init() diff --git a/src/cec-client/env.h.in b/src/cec-client/env.h.in index 2f3fd71d..550c8f17 100644 --- a/src/cec-client/env.h.in +++ b/src/cec-client/env.h.in @@ -34,7 +34,7 @@ */ #include "cectypes.h" -#include +#include "p8-platform/os.h" #ifdef UNUSED #elif defined(__GNUC__) diff --git a/src/cecc-client/env.h.in b/src/cecc-client/env.h.in index 9b80c715..16bac0ba 100644 --- a/src/cecc-client/env.h.in +++ b/src/cecc-client/env.h.in @@ -34,7 +34,7 @@ */ #include "cectypes.h" -#include +#include "p8-platform/os.h" #ifdef UNUSED #elif defined(__GNUC__) diff --git a/src/libcec/CECClient.h b/src/libcec/CECClient.h index 49528eef..a1e8b530 100644 --- a/src/libcec/CECClient.h +++ b/src/libcec/CECClient.h @@ -34,10 +34,10 @@ #include "env.h" #include "LibCEC.h" +#include "p8-platform/threads/threads.h" +#include "p8-platform/util/buffer.h" +#include "p8-platform/threads/mutex.h" #include -#include -#include -#include #include namespace CEC diff --git a/src/libcec/CECInputBuffer.h b/src/libcec/CECInputBuffer.h index 89251f7b..4b604993 100644 --- a/src/libcec/CECInputBuffer.h +++ b/src/libcec/CECInputBuffer.h @@ -33,8 +33,8 @@ */ #include "env.h" -#include -#include +#include "p8-platform/threads/mutex.h" +#include "p8-platform/util/buffer.h" namespace CEC { diff --git a/src/libcec/CECProcessor.cpp b/src/libcec/CECProcessor.cpp index 8ce46c52..44caa164 100644 --- a/src/libcec/CECProcessor.cpp +++ b/src/libcec/CECProcessor.cpp @@ -45,8 +45,8 @@ #include "LibCEC.h" #include "CECClient.h" #include "CECTypeUtils.h" -#include -#include +#include "p8-platform/util/timeutils.h" +#include "p8-platform/util/util.h" #include using namespace CEC; diff --git a/src/libcec/CECProcessor.h b/src/libcec/CECProcessor.h index 08917b93..20d792a9 100644 --- a/src/libcec/CECProcessor.h +++ b/src/libcec/CECProcessor.h @@ -34,10 +34,8 @@ #include "env.h" #include - -#include -#include - +#include "p8-platform/threads/threads.h" +#include "p8-platform/util/buffer.h" #include "adapter/AdapterCommunication.h" #include "devices/CECDeviceMap.h" #include "CECInputBuffer.h" diff --git a/src/libcec/CECTypeUtils.h b/src/libcec/CECTypeUtils.h index 0d0cf178..25c1c6e3 100644 --- a/src/libcec/CECTypeUtils.h +++ b/src/libcec/CECTypeUtils.h @@ -33,7 +33,7 @@ */ #include "env.h" -#include +#include "p8-platform/util/StringUtils.h" namespace CEC { diff --git a/src/libcec/LibCEC.cpp b/src/libcec/LibCEC.cpp index 8eedaa7b..d241cb65 100644 --- a/src/libcec/LibCEC.cpp +++ b/src/libcec/LibCEC.cpp @@ -41,8 +41,8 @@ #include "devices/CECBusDevice.h" #include "devices/CECPlaybackDevice.h" #include "devices/CECTV.h" -#include -#include +#include "p8-platform/util/timeutils.h" +#include "p8-platform/util/util.h" #include #include diff --git a/src/libcec/LibCEC.h b/src/libcec/LibCEC.h index 7155926c..46f4a316 100644 --- a/src/libcec/LibCEC.h +++ b/src/libcec/LibCEC.h @@ -35,7 +35,7 @@ #include "env.h" #include #include "cec.h" -#include +#include "p8-platform/util/buffer.h" #include "CECTypeUtils.h" #include diff --git a/src/libcec/SwigHelper.h b/src/libcec/SwigHelper.h index c9d3e1cd..464b7d9c 100644 --- a/src/libcec/SwigHelper.h +++ b/src/libcec/SwigHelper.h @@ -40,7 +40,7 @@ #include "cectypes.h" #include "cec.h" #include "CECTypeUtils.h" -#include +#include "p8-platform/threads/mutex.h" /** XXX only to keep the IDE happy, using the actual Python.h with the correct system version when building */ #ifndef Py_PYTHON_H #include diff --git a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp index 538938e0..3248b35f 100644 --- a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp +++ b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.cpp @@ -42,7 +42,7 @@ #include "AOCECAdapterCommunication.h" #include "CECTypeUtils.h" #include "LibCEC.h" -#include +#include "p8-platform/util/buffer.h" using namespace CEC; using namespace P8PLATFORM; diff --git a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h index 65964025..b7fa9a66 100644 --- a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h +++ b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h @@ -38,8 +38,8 @@ #if defined(HAVE_AOCEC_API) -#include -#include +#include "p8-platform/threads/mutex.h" +#include "p8-platform/threads/threads.h" #include "../AdapterCommunication.h" #include diff --git a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.cpp b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.cpp index 44cf4998..251510c3 100644 --- a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.cpp +++ b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.cpp @@ -42,7 +42,7 @@ #include "CECTypeUtils.h" #include "LibCEC.h" -#include +#include "p8-platform/util/buffer.h" using namespace CEC; using namespace P8PLATFORM; diff --git a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h index 9292fdcb..728fddfe 100644 --- a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h +++ b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h @@ -36,8 +36,8 @@ #if defined(HAVE_EXYNOS_API) -#include -#include +#include "p8-platform/threads/mutex.h" +#include "p8-platform/threads/threads.h" #include "../AdapterCommunication.h" #include diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp index a271f74a..19eaa169 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp @@ -39,7 +39,7 @@ #include "LibCEC.h" #include "CECProcessor.h" #include "CECTypeUtils.h" -#include +#include "p8-platform/util/util.h" #include using namespace CEC; diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h index 60a112a1..181f9443 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h @@ -33,7 +33,7 @@ */ #include "env.h" -#include +#include "p8-platform/threads/mutex.h" #include "USBCECAdapterMessage.h" namespace CEC diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp index ee01c889..c30fa7a5 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp @@ -39,8 +39,8 @@ #include "USBCECAdapterMessage.h" #include "USBCECAdapterDetection.h" #include "platform/sockets/serialport.h" -#include -#include +#include "p8-platform/util/timeutils.h" +#include "p8-platform/util/util.h" #include "platform/util/edid.h" #include "platform/adl/adl-edid.h" #include "platform/nvidia/nv-edid.h" diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h index d5096cc5..589ef7b5 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h @@ -33,7 +33,7 @@ */ #include "env.h" -#include +#include "p8-platform/threads/threads.h" #include "adapter/AdapterCommunication.h" #include "USBCECAdapterMessage.h" diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp index 5d9ee9bf..f2fe2f59 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp @@ -71,7 +71,7 @@ extern "C" { #include #include #include -#include +#include "p8-platform/util/StringUtils.h" #define CEC_VID 0x2548 #define CEC_PID 0x1001 diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp index 1ccac14b..e4540355 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp @@ -36,7 +36,7 @@ #include "USBCECAdapterCommunication.h" #include "USBCECAdapterMessage.h" -#include +#include "p8-platform/sockets/socket.h" #include "LibCEC.h" using namespace CEC; diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.h index c71deed9..c7cf1ddc 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.h @@ -33,9 +33,9 @@ */ #include "env.h" -#include -#include -#include +#include "p8-platform/threads/threads.h" +#include "p8-platform/util/buffer.h" +#include "p8-platform/util/timeutils.h" #include #include "USBCECAdapterMessage.h" diff --git a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h index 411f224c..9406ebd4 100644 --- a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h +++ b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h @@ -36,7 +36,7 @@ #if defined(HAVE_RPI_API) #include "adapter/AdapterCommunication.h" -#include +#include "p8-platform/threads/threads.h" #define RPI_ADAPTER_VID 0x2708 #define RPI_ADAPTER_PID 0x1001 diff --git a/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.cpp b/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.cpp index 4927ec62..4d140d28 100644 --- a/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.cpp +++ b/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.cpp @@ -35,7 +35,7 @@ #if defined(HAVE_RPI_API) #include "RPiCECAdapterMessageQueue.h" -#include +#include "p8-platform/util/StringUtils.h" // use vc_cec_send_message2() if defined and vc_cec_send_message() if not //#define RPI_USE_SEND_MESSAGE2 diff --git a/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.h b/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.h index 3d3148ba..cf892a31 100644 --- a/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.h +++ b/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.h @@ -33,7 +33,7 @@ */ #include "env.h" -#include +#include "p8-platform/util/buffer.h" #include #include "adapter/AdapterCommunication.h" diff --git a/src/libcec/adapter/TDA995x/AdapterMessageQueue.h b/src/libcec/adapter/TDA995x/AdapterMessageQueue.h index ea46e651..bbba243c 100644 --- a/src/libcec/adapter/TDA995x/AdapterMessageQueue.h +++ b/src/libcec/adapter/TDA995x/AdapterMessageQueue.h @@ -33,7 +33,7 @@ */ #include "env.h" -#include +#include "p8-platform/threads/mutex.h" namespace CEC { diff --git a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp index 377a5743..15453cbc 100644 --- a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp +++ b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp @@ -38,8 +38,8 @@ #include "CECTypeUtils.h" #include "LibCEC.h" -#include -#include +#include "p8-platform/sockets/cdevsocket.h" +#include "p8-platform/util/buffer.h" extern "C" { #define __cec_h__ diff --git a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h index 326b3cf6..d6f7aa34 100644 --- a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h +++ b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h @@ -35,9 +35,9 @@ #include "env.h" #if defined(HAVE_TDA995X_API) -#include -#include -#include +#include "p8-platform/threads/mutex.h" +#include "p8-platform/threads/threads.h" +#include "p8-platform/sockets/socket.h" #include "adapter/AdapterCommunication.h" #include diff --git a/src/libcec/devices/CECBusDevice.cpp b/src/libcec/devices/CECBusDevice.cpp index 99f762c6..79e704a3 100644 --- a/src/libcec/devices/CECBusDevice.cpp +++ b/src/libcec/devices/CECBusDevice.cpp @@ -46,8 +46,8 @@ #include "implementations/AQCommandHandler.h" #include "LibCEC.h" #include "CECTypeUtils.h" -#include -#include +#include "p8-platform/util/timeutils.h" +#include "p8-platform/util/util.h" #include "CECAudioSystem.h" #include "CECPlaybackDevice.h" diff --git a/src/libcec/devices/CECBusDevice.h b/src/libcec/devices/CECBusDevice.h index b8255aab..62adef4c 100644 --- a/src/libcec/devices/CECBusDevice.h +++ b/src/libcec/devices/CECBusDevice.h @@ -33,10 +33,10 @@ */ #include "env.h" +#include "p8-platform/threads/mutex.h" #include #include #include -#include #include namespace CEC diff --git a/src/libcec/devices/CECPlaybackDevice.cpp b/src/libcec/devices/CECPlaybackDevice.cpp index 58301754..9f3d60dd 100644 --- a/src/libcec/devices/CECPlaybackDevice.cpp +++ b/src/libcec/devices/CECPlaybackDevice.cpp @@ -33,7 +33,6 @@ #include "env.h" #include "CECPlaybackDevice.h" - #include "implementations/CECCommandHandler.h" #include "CECProcessor.h" #include "LibCEC.h" diff --git a/src/libcec/env.h.in b/src/libcec/env.h.in index 0774a1c7..456a2e75 100644 --- a/src/libcec/env.h.in +++ b/src/libcec/env.h.in @@ -34,7 +34,7 @@ */ #include "cectypes.h" -#include +#include "p8-platform/os.h" #ifdef UNUSED #elif defined(__GNUC__) diff --git a/src/libcec/implementations/AQCommandHandler.h b/src/libcec/implementations/AQCommandHandler.h index b9a7ba49..4d1cc89e 100644 --- a/src/libcec/implementations/AQCommandHandler.h +++ b/src/libcec/implementations/AQCommandHandler.h @@ -34,7 +34,7 @@ #include "env.h" #include "CECCommandHandler.h" -#include +#include "p8-platform/threads/threads.h" namespace CEC { diff --git a/src/libcec/implementations/CECCommandHandler.cpp b/src/libcec/implementations/CECCommandHandler.cpp index d3036131..f3bbf4a0 100644 --- a/src/libcec/implementations/CECCommandHandler.cpp +++ b/src/libcec/implementations/CECCommandHandler.cpp @@ -41,7 +41,7 @@ #include "CECProcessor.h" #include "LibCEC.h" #include "CECTypeUtils.h" -#include +#include "p8-platform/util/util.h" using namespace CEC; using namespace P8PLATFORM; diff --git a/src/libcec/implementations/CECCommandHandler.h b/src/libcec/implementations/CECCommandHandler.h index 8ff9a1e8..f2d8db46 100644 --- a/src/libcec/implementations/CECCommandHandler.h +++ b/src/libcec/implementations/CECCommandHandler.h @@ -36,7 +36,7 @@ #include #include #include -#include +#include "p8-platform/threads/mutex.h" namespace CEC { diff --git a/src/libcec/implementations/PHCommandHandler.h b/src/libcec/implementations/PHCommandHandler.h index 2e371542..3a177446 100644 --- a/src/libcec/implementations/PHCommandHandler.h +++ b/src/libcec/implementations/PHCommandHandler.h @@ -34,7 +34,7 @@ #include "env.h" #include "CECCommandHandler.h" -#include +#include "p8-platform/threads/threads.h" namespace CEC { diff --git a/src/libcec/implementations/RLCommandHandler.cpp b/src/libcec/implementations/RLCommandHandler.cpp index 72c4be1c..b1474eed 100644 --- a/src/libcec/implementations/RLCommandHandler.cpp +++ b/src/libcec/implementations/RLCommandHandler.cpp @@ -34,7 +34,7 @@ #include "env.h" #include "RLCommandHandler.h" -#include +#include "p8-platform/util/timeutils.h" #include "devices/CECBusDevice.h" #include "CECProcessor.h" #include "LibCEC.h" diff --git a/src/libcec/implementations/SLCommandHandler.cpp b/src/libcec/implementations/SLCommandHandler.cpp index dbdd01ce..3cb02b6f 100644 --- a/src/libcec/implementations/SLCommandHandler.cpp +++ b/src/libcec/implementations/SLCommandHandler.cpp @@ -34,7 +34,7 @@ #include "env.h" #include "SLCommandHandler.h" -#include +#include "p8-platform/util/timeutils.h" #include "devices/CECBusDevice.h" #include "devices/CECPlaybackDevice.h" #include "CECProcessor.h" diff --git a/src/libcec/platform/adl/adl-edid.cpp b/src/libcec/platform/adl/adl-edid.cpp index 89978cb8..9281d349 100644 --- a/src/libcec/platform/adl/adl-edid.cpp +++ b/src/libcec/platform/adl/adl-edid.cpp @@ -38,7 +38,7 @@ // for dlsym and friends #if defined(__WINDOWS__) -#include +#include "p8-platform/windows/dlfcn-win32.h" #endif using namespace P8PLATFORM; diff --git a/src/libcec/platform/drm/drm-edid.cpp b/src/libcec/platform/drm/drm-edid.cpp index 77662b5b..a3b15969 100644 --- a/src/libcec/platform/drm/drm-edid.cpp +++ b/src/libcec/platform/drm/drm-edid.cpp @@ -33,7 +33,7 @@ #include "env.h" #ifdef HAVE_DRM_EDID_PARSER -#include +#include "p8-platform/os.h" #include "drm-edid.h" #include #include diff --git a/src/libcec/platform/nvidia/nv-edid.cpp b/src/libcec/platform/nvidia/nv-edid.cpp index cfa552ff..f868490b 100644 --- a/src/libcec/platform/nvidia/nv-edid.cpp +++ b/src/libcec/platform/nvidia/nv-edid.cpp @@ -36,7 +36,7 @@ #if defined(HAVE_NVIDIA_EDID_PARSER) -#include +#include "p8-platform/os.h" #include using namespace P8PLATFORM; diff --git a/src/libcec/platform/posix/serialport.cpp b/src/libcec/platform/posix/serialport.cpp index a3754e5a..c97bd69f 100644 --- a/src/libcec/platform/posix/serialport.cpp +++ b/src/libcec/platform/posix/serialport.cpp @@ -36,7 +36,7 @@ #include #include "../sockets/serialport.h" #include "../util/baudrate.h" -#include +#include "p8-platform/posix/os-socket.h" #if defined(__APPLE__) || defined(__FreeBSD__) #ifndef XCASE diff --git a/src/libcec/platform/sockets/serialport.h b/src/libcec/platform/sockets/serialport.h index ae1436e9..1800a751 100644 --- a/src/libcec/platform/sockets/serialport.h +++ b/src/libcec/platform/sockets/serialport.h @@ -33,7 +33,7 @@ */ #include "env.h" -#include +#include "p8-platform/util/buffer.h" #include #include @@ -42,7 +42,7 @@ #include #endif -#include +#include "p8-platform/sockets/socket.h" namespace P8PLATFORM { diff --git a/src/libcec/platform/windows/serialport.cpp b/src/libcec/platform/windows/serialport.cpp index 94d0a8bd..5da828f7 100644 --- a/src/libcec/platform/windows/serialport.cpp +++ b/src/libcec/platform/windows/serialport.cpp @@ -33,7 +33,7 @@ #include "../sockets/serialport.h" #include "../util/baudrate.h" -#include +#include "p8-platform/util/timeutils.h" using namespace P8PLATFORM; From f4e2ed77a1f4317febb9ff43ab99807ec9941ce8 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 15 Mar 2017 09:37:05 +0100 Subject: [PATCH 04/93] fixed: detect debian based distros properly when installing python. closes #314 --- src/libcec/cmake/CheckPlatformSupport.cmake | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index 6875606c..962607e5 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -194,11 +194,18 @@ else() DESTINATION python/cec RENAME __init__.py) else() - if(EXISTS "/etc/lsb-release") - SET(PYTHON_PKG_DIR "dist-packages") - else() + if(EXISTS "/etc/os-release") + file(READ "/etc/os-release" OS_RELEASE) + string(REGEX MATCH "ID(_LIKE)?=debian" IS_DEBIAN ${OS_RELEASE}) + if (IS_DEBIAN) + SET(PYTHON_PKG_DIR "dist-packages") + endif() + endif() + + if (NOT PYTHON_PKG_DIR) SET(PYTHON_PKG_DIR "site-packages") endif() + install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}/cec) install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cec.py From e1df683b4248f15fe22f77d08d8f7e48b779c64c Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 15 Mar 2017 09:37:58 +0100 Subject: [PATCH 05/93] fixed: don't filter out broadcast in HandleDeviceVendorCommandWithId(). issue #309 --- src/libcec/implementations/ANCommandHandler.cpp | 2 +- src/libcec/implementations/RLCommandHandler.cpp | 2 +- src/libcec/implementations/SLCommandHandler.cpp | 4 ++-- src/libcec/implementations/VLCommandHandler.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libcec/implementations/ANCommandHandler.cpp b/src/libcec/implementations/ANCommandHandler.cpp index 89769e6a..e6f3fd26 100644 --- a/src/libcec/implementations/ANCommandHandler.cpp +++ b/src/libcec/implementations/ANCommandHandler.cpp @@ -91,7 +91,7 @@ bool CANCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_ int CANCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &command) { - if (!m_processor->IsHandledByLibCEC(command.destination)) + if (!m_processor->IsHandledByLibCEC(command.destination) && command.destination != CECDEVICE_BROADCAST) return CEC_ABORT_REASON_INVALID_OPERAND; // samsung's vendor id diff --git a/src/libcec/implementations/RLCommandHandler.cpp b/src/libcec/implementations/RLCommandHandler.cpp index b1474eed..e905dc68 100644 --- a/src/libcec/implementations/RLCommandHandler.cpp +++ b/src/libcec/implementations/RLCommandHandler.cpp @@ -87,7 +87,7 @@ bool CRLCommandHandler::InitHandler(void) int CRLCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &command) { - if (!m_processor->IsHandledByLibCEC(command.destination)) + if (!m_processor->IsHandledByLibCEC(command.destination) && command.destination != CECDEVICE_BROADCAST) return CEC_ABORT_REASON_INVALID_OPERAND; if (command.parameters.size < 4) diff --git a/src/libcec/implementations/SLCommandHandler.cpp b/src/libcec/implementations/SLCommandHandler.cpp index 3cb02b6f..386ff398 100644 --- a/src/libcec/implementations/SLCommandHandler.cpp +++ b/src/libcec/implementations/SLCommandHandler.cpp @@ -105,8 +105,8 @@ bool CSLCommandHandler::InitHandler(void) int CSLCommandHandler::HandleVendorCommand(const cec_command &command) { - if (!m_processor->IsHandledByLibCEC(command.destination)) - return true; + if (!m_processor->IsHandledByLibCEC(command.destination) && command.destination != CECDEVICE_BROADCAST) + return COMMAND_HANDLED; if (command.parameters.size == 1 && command.parameters[0] == SL_COMMAND_INIT) diff --git a/src/libcec/implementations/VLCommandHandler.cpp b/src/libcec/implementations/VLCommandHandler.cpp index bbdcbf93..4ccf6dd3 100644 --- a/src/libcec/implementations/VLCommandHandler.cpp +++ b/src/libcec/implementations/VLCommandHandler.cpp @@ -103,7 +103,7 @@ bool CVLCommandHandler::InitHandler(void) int CVLCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &command) { - if (!m_processor->IsHandledByLibCEC(command.destination)) + if (!m_processor->IsHandledByLibCEC(command.destination) && command.destination != CECDEVICE_BROADCAST) return CEC_ABORT_REASON_INVALID_OPERAND; if (command.parameters[0] != 0x00 || From 67d444dd944192a31ab07637a4b57d27c890ede4 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 15 Mar 2017 11:36:00 +0100 Subject: [PATCH 06/93] fixed: send an active source message when a routing change has been received with libCEC's address as new route and no active source message has been sent yet. issue #309 #205 #233 --- src/libcec/devices/CECBusDevice.cpp | 19 +++++++++++++++++-- src/libcec/devices/CECBusDevice.h | 4 ++++ src/libcec/devices/CECDeviceMap.cpp | 6 ++++++ src/libcec/devices/CECDeviceMap.h | 1 + 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/libcec/devices/CECBusDevice.cpp b/src/libcec/devices/CECBusDevice.cpp index 79e704a3..4c0ee880 100644 --- a/src/libcec/devices/CECBusDevice.cpp +++ b/src/libcec/devices/CECBusDevice.cpp @@ -153,7 +153,8 @@ CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogi m_bAwaitingReceiveFailed(false), m_bVendorIdRequested (false), m_waitForResponse (new CWaitForResponse), - m_bImageViewOnSent (false) + m_bImageViewOnSent (false), + m_bActiveSourceSent (false) { m_handler = new CCECCommandHandler(this); m_strDeviceName = ToString(m_iLogicalAddress); @@ -685,6 +686,9 @@ void CCECBusDevice::SetPowerStatus(const cec_power_status powerStatus) m_iLastPowerStateUpdate = GetTimeMs(); LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): power status changed from '%s' to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_powerStatus), ToString(powerStatus)); m_powerStatus = powerStatus; + + if (m_iLogicalAddress == CECDEVICE_TV) + m_processor->GetDevices()->ResetActiveSourceSent(); } } @@ -1141,6 +1145,7 @@ bool CCECBusDevice::TransmitActiveSource(bool bIsReply) if (bSendActiveSource) { MarkBusy(); + SetActiveSourceSent(true); bActiveSourceSent = m_handler->TransmitActiveSource(m_iLogicalAddress, iPhysicalAddress, bIsReply); MarkReady(); } @@ -1215,7 +1220,7 @@ void CCECBusDevice::SetActiveRoute(uint16_t iRoute) return; CCECBusDevice* newRoute = m_processor->GetDeviceByPhysicalAddress(iRoute, true); - if (newRoute && newRoute->IsHandledByLibCEC() && !newRoute->IsActiveSource()) + if (newRoute && newRoute->IsHandledByLibCEC() && (!ActiveSourceSent() || !newRoute->IsActiveSource())) { // we were made the active source, send notification newRoute->ActivateSource(); @@ -1498,3 +1503,13 @@ bool CCECBusDevice::TransmitMuteAudio(const cec_logical_address source) return TransmitKeypress(source, CEC_USER_CONTROL_CODE_MUTE) && TransmitKeyRelease(source); } + +void CCECBusDevice::SetActiveSourceSent(bool setto = true) +{ + m_bActiveSource = setto; +} + +bool CCECBusDevice::ActiveSourceSent(void) const +{ + return m_bActiveSourceSent; +} diff --git a/src/libcec/devices/CECBusDevice.h b/src/libcec/devices/CECBusDevice.h index 62adef4c..ac825961 100644 --- a/src/libcec/devices/CECBusDevice.h +++ b/src/libcec/devices/CECBusDevice.h @@ -199,6 +199,9 @@ namespace CEC void SignalOpcode(cec_opcode opcode); bool WaitForOpcode(cec_opcode opcode); + void SetActiveSourceSent(bool setto = true); + bool ActiveSourceSent(void) const; + CCECAudioSystem * AsAudioSystem(void); static CCECAudioSystem * AsAudioSystem(CCECBusDevice *device); CCECPlaybackDevice * AsPlaybackDevice(void); @@ -243,5 +246,6 @@ namespace CEC bool m_bVendorIdRequested; CWaitForResponse *m_waitForResponse; bool m_bImageViewOnSent; + bool m_bActiveSourceSent; }; }; diff --git a/src/libcec/devices/CECDeviceMap.cpp b/src/libcec/devices/CECDeviceMap.cpp index 410893a1..4b515924 100644 --- a/src/libcec/devices/CECDeviceMap.cpp +++ b/src/libcec/devices/CECDeviceMap.cpp @@ -212,6 +212,12 @@ CCECBusDevice *CCECDeviceMap::GetActiveSource(void) const return NULL; } +void CCECDeviceMap::ResetActiveSourceSent(void) +{ + for (CECDEVICEMAP::iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++) + it->second->SetActiveSourceSent(false); +} + void CCECDeviceMap::FilterLibCECControlled(CECDEVICEVEC &devices) { CECDEVICEVEC newDevices; diff --git a/src/libcec/devices/CECDeviceMap.h b/src/libcec/devices/CECDeviceMap.h index f8b4a159..34a1da27 100644 --- a/src/libcec/devices/CECDeviceMap.h +++ b/src/libcec/devices/CECDeviceMap.h @@ -71,6 +71,7 @@ namespace CEC void GetWakeDevices(const libcec_configuration &configuration, CECDEVICEVEC &devices) const; CCECBusDevice *GetActiveSource(void) const; + void ResetActiveSourceSent(void); static void FilterLibCECControlled(CECDEVICEVEC &devices); static void FilterActive(CECDEVICEVEC &devices); From d1a708b9401cc20efd44f8549edffc972e4786b0 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 15 Mar 2017 11:38:32 +0100 Subject: [PATCH 07/93] fixed 67d444dd944192a31ab07637a4b57d27c890ede4 --- src/libcec/devices/CECBusDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/devices/CECBusDevice.cpp b/src/libcec/devices/CECBusDevice.cpp index 4c0ee880..d71ac0e1 100644 --- a/src/libcec/devices/CECBusDevice.cpp +++ b/src/libcec/devices/CECBusDevice.cpp @@ -1504,7 +1504,7 @@ bool CCECBusDevice::TransmitMuteAudio(const cec_logical_address source) TransmitKeyRelease(source); } -void CCECBusDevice::SetActiveSourceSent(bool setto = true) +void CCECBusDevice::SetActiveSourceSent(bool setto /* = true */) { m_bActiveSource = setto; } From 8563411d1b449edab6edb52b7beba053c1106ec0 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 15 Mar 2017 13:09:06 +0100 Subject: [PATCH 08/93] fixed: vs2015 c++ redistributables --- project/libCEC.nsi | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/project/libCEC.nsi b/project/libCEC.nsi index 9e807bcd..d124cbda 100644 --- a/project/libCEC.nsi +++ b/project/libCEC.nsi @@ -225,39 +225,39 @@ Section "" SecEvGhostCec ${EndIf} SectionEnd -!define REDISTRIBUTABLE_X86_SECTIONNAME "Microsoft Visual C++ 2013 Redistributable Package (x86)" +!define REDISTRIBUTABLE_X86_SECTIONNAME "Microsoft Visual C++ 2015 Redistributable Package (x86)" Section "" SecVCRedistX86 SetShellVarContext current SectionIn 1 3 - SetOutPath "$TEMP\vc2013_x86" + SetOutPath "$TEMP\vc2015_x86" ${If} $VSRedistInstalledX86 != "Yes" - NSISdl::download http://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x86.exe vcredist_x86.exe - ExecWait '"$TEMP\vc2013_x86\vcredist_x86.exe" /q' $VSRedistSetupError + NSISdl::download https://download.microsoft.com/download/6/D/F/6DF3FF94-F7F9-4F0B-838C-A328D1A7D0EE/vc_redist.x86.exe vc_redist.x86.exe + ExecWait '"$TEMP\vc2015_x86\vc_redist.x86.exe" /q' $VSRedistSetupError ${Endif} - RMDIR /r "$TEMP\vc2013_x86" + RMDIR /r "$TEMP\vc2015_x86" SectionEnd -!define REDISTRIBUTABLE_X64_SECTIONNAME "Microsoft Visual C++ 2013 Redistributable Package (x64)" +!define REDISTRIBUTABLE_X64_SECTIONNAME "Microsoft Visual C++ 2015 Redistributable Package (x64)" Section "" SecVCRedistX64 SetShellVarContext current SectionIn 1 3 - SetOutPath "$TEMP\vc2013_x64" + SetOutPath "$TEMP\vc2015_x64" ${If} $VSRedistInstalledX64 != "Yes" - NSISdl::download http://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe vcredist_x64.exe - ExecWait '"$TEMP\vc2013_x64\vcredist_x64.exe" /q' $VSRedistSetupError + NSISdl::download https://download.microsoft.com/download/6/D/F/6DF3FF94-F7F9-4F0B-838C-A328D1A7D0EE/vc_redist.x64.exe vc_redist.x64.exe + ExecWait '"$TEMP\vc2015_x64\vc_redist.x64.exe" /q' $VSRedistSetupError ${Endif} - RMDIR /r "$TEMP\vc2013_x64" + RMDIR /r "$TEMP\vc2015_x64" SectionEnd Function .onInit ; check for vc2013 x86 redist - ReadRegDword $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{f65db027-aff3-4070-886a-0d87064aabb1}" "BundleVersion" + ReadRegDword $1 HKLM "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{37B55901-995A-3650-80B1-BBFD047E2911}" "BundleVersion" ${If} $1 != "" StrCpy $VSRedistInstalledX86 "Yes" ${Endif} @@ -271,8 +271,8 @@ Function .onInit ${Endif} ${If} ${RunningX64} - ; check for vc2013 x64 redist - ReadRegDword $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{050d4fc8-5d48-4b8f-8972-47c82c46020f}" "BundleVersion" + ; check for vc2015 x64 redist + ReadRegDword $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{FAAD7243-0141-3987-AA2F-E56B20F80E41}" "BundleVersion" ${If} $1 != "" StrCpy $VSRedistInstalledX64 "Yes" ${Endif} From 3953f8dc38ac204216802de9ae23e74324fab441 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Fri, 7 Apr 2017 11:57:58 +0200 Subject: [PATCH 09/93] fixed typo --- project/libCEC.nsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/libCEC.nsi b/project/libCEC.nsi index d124cbda..9fb11800 100644 --- a/project/libCEC.nsi +++ b/project/libCEC.nsi @@ -299,7 +299,7 @@ Function .onInit !insertMacro UnSelectSection ${SecEvGhostCec} SectionSetText ${SecEvGhostCec} "" MessageBox MB_OK \ - "EventGhost was found found, so the plugin for EventGhost will not be installed. You can download EventGhost from http://www.eventghost.org/" + "EventGhost was not found, so the plugin for EventGhost will not be installed. You can download EventGhost from http://www.eventghost.org/" ${Endif} FunctionEnd From 13af1b6d27a398141a0f31f1ad01eb544f3084b3 Mon Sep 17 00:00:00 2001 From: Daniel Rogers Date: Tue, 31 Jan 2017 09:17:47 -0800 Subject: [PATCH 10/93] Add a Reinitialze action. Useful for after the device has been powered down --- src/EventGhost/__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/EventGhost/__init__.py b/src/EventGhost/__init__.py index 146ad168..f837659b 100644 --- a/src/EventGhost/__init__.py +++ b/src/EventGhost/__init__.py @@ -116,6 +116,9 @@ def __init__(self): self.AddActionsFromList(QUERIES) def __start__(self, connectedAvr, portNumber, deviceName): + self.lastConnectedAvr = connectedAvr + self.lastPortNumber = portNumber + self.lastDeviceName = deviceName if self.InitLibCec(connectedAvr, portNumber, deviceName): # print libCEC version and compilation information print("libCEC version " + self.lib.VersionToString(self.cecconfig.serverVersion) + " loaded: " + self.lib.GetLibInfo()) @@ -139,6 +142,13 @@ def __start__(self, connectedAvr, portNumber, deviceName): else: print("Couldn't initialise libCEC. Please check your configuration.") + def ReinitLibCec(self): + self.lib.Close() + if self.InitLibCec(self.lastConnectedAvr, self.lastPortNumber, self.lastDeviceName): + print "libCEC reinited sucessfully" + else: + print("Could not reinit libCEC") + def __stop__(self): self.lib.Close() @@ -321,6 +331,7 @@ def cbAddressesChanged(event = None): (ActionNoParam, 'ToggleMute', 'Toggle volume mute', 'Send a mute toggle command to the AVR (if present)', u'self.lib.MuteAudio()'), (ActionParamString, 'RawCommand', 'Send command', 'Send a raw CEC command', u'self.lib.Transmit(self.lib.CommandFromString(\'{0}\'))'), + (ActionNoParam, 'ReinitLibCec', 'Re-initialize Libcec', 'Useful if the device was powered down', u'self.ReinitLibCec()'), ) QUERIES = ( From ba979114c39f35ab63021640bef52b7d2d2788ad Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 5 Feb 2017 10:00:56 +0100 Subject: [PATCH 11/93] TDA995x: Fix logical address readback Signed-off-by: Rudi --- src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp index 15453cbc..6bc42d9f 100644 --- a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp +++ b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp @@ -268,7 +268,8 @@ cec_logical_addresses CTDA995xCECAdapterCommunication::GetLogicalAddresses(void) for (int la = CECDEVICE_TV; la < CECDEVICE_BROADCAST; la++) { - m_logicalAddresses.Set(cec_logical_address(la)); + if ((info.LogicalAddressMask >> la) & 1) + m_logicalAddresses.Set(cec_logical_address(la)); } } From d2cc944735d7eaf7efc050090e788012ec744e29 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 27 Nov 2016 16:53:53 +0100 Subject: [PATCH 12/93] TDA995x: Handle physical address change, optimize logical address setup For neccessary kernel driver updates refer to: https://github.com/warped-rudi/linux-cubox/commit/dfe8d491728fd656572540138638914fa26a56d4 Signed-off-by: Rudi --- .../TDA995x/TDA995xCECAdapterCommunication.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp index 6bc42d9f..5d115b2c 100644 --- a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp +++ b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.cpp @@ -295,7 +295,7 @@ bool CTDA995xCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addr all_addresses.SwitchOn = addresses.AckMask() & 0x7fff; all_addresses.SwitchOff = ~all_addresses.SwitchOn; - if (all_addresses.SwitchOn != (1 << addresses.primary) && + if (all_addresses.SwitchOn != ((1 << addresses.primary) & 0x7fff) && m_dev->Ioctl(CEC_IOCTL_SET_RX_ADDR_MASK, &all_addresses) != 0) { LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: CEC_IOCTL_SET_RX_ADDR_MASK failed !", __func__); @@ -364,6 +364,18 @@ void *CTDA995xCECAdapterCommunication::Process(void) if (!bHandled) LIB_CEC->AddLog(CEC_LOG_WARNING, "%s: unhandled response received !", __func__); } + else if (frame.service == CEC_HPD_PKT) + { + LIB_CEC->AddLog(CEC_LOG_NOTICE, "%s: physical address %d.%d.%d.%d %s.", __func__, + frame.data[2] >> 4, frame.data[2] & 0xf, frame.data[1] >> 4, frame.data[1] & 0xf, + frame.data[0] ? "disconnected" : "connected" ); + + if (frame.size >= 6 && frame.data[0] == 0) + { + uint16_t iNewAddress = (frame.data[2] << 8) | frame.data[1]; + m_callback->HandlePhysicalAddressChanged(iNewAddress); + } + } } } From 2fa258a8b7d8a4c5a1c9975cc71288e468e80229 Mon Sep 17 00:00:00 2001 From: Rechi Date: Wed, 15 Mar 2017 17:43:25 +0100 Subject: [PATCH 13/93] detect WIN64 in cmake automatically --- src/libcec/cmake/CheckPlatformSupport.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index 962607e5..19cb8569 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -21,6 +21,7 @@ set(RPI_INCLUDE_DIR "" CACHE STRING "Path to Raspberry Pi headers") set(PLATFORM_LIBREQUIRES "") include(CheckFunctionExists) +include(CheckSymbolExists) include(FindPkgConfig) # defaults @@ -42,6 +43,7 @@ if(WIN32) # Windows add_definitions(-DTARGET_WINDOWS -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -D_WINSOCKAPI_) set(LIB_DESTINATION ".") + check_symbol_exists(_X64_ Windows.h WIN64) if (${WIN64}) set(LIB_INFO "${LIB_INFO} (x64)") else() From ab621bd1d61f7300a3e07a8fedd79f1acab65723 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Fri, 14 Apr 2017 19:41:03 +0200 Subject: [PATCH 14/93] fix missing tinfo linking in cec-client --- src/cec-client/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cec-client/CMakeLists.txt b/src/cec-client/CMakeLists.txt index 2cb42bb3..37c733b3 100644 --- a/src/cec-client/CMakeLists.txt +++ b/src/cec-client/CMakeLists.txt @@ -44,6 +44,7 @@ if (NOT WIN32) # curses if (HAVE_CURSES_API) target_link_libraries(cec-client curses) + target_link_libraries(cec-client tinfo) endif() # rt From af50b2943db612121a3003937062d3acf0397fa6 Mon Sep 17 00:00:00 2001 From: flubshi Date: Fri, 26 May 2017 02:39:07 +0200 Subject: [PATCH 15/93] Fix: set wrong variable This fixes many cec reconnect problems --- src/libcec/devices/CECBusDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/devices/CECBusDevice.cpp b/src/libcec/devices/CECBusDevice.cpp index d71ac0e1..05925a07 100644 --- a/src/libcec/devices/CECBusDevice.cpp +++ b/src/libcec/devices/CECBusDevice.cpp @@ -1506,7 +1506,7 @@ bool CCECBusDevice::TransmitMuteAudio(const cec_logical_address source) void CCECBusDevice::SetActiveSourceSent(bool setto /* = true */) { - m_bActiveSource = setto; + m_bActiveSourceSent = setto; } bool CCECBusDevice::ActiveSourceSent(void) const From 031ed00da0a95b960106d2001ba9e92b25940981 Mon Sep 17 00:00:00 2001 From: "J. Longman" Date: Mon, 3 Apr 2017 12:59:25 -0400 Subject: [PATCH 16/93] Update README.developers.md Make URLs match location in current repo, branch. I did this before seeing #325 - my update also includes updating C++ which #325 does not. --- docs/README.developers.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/README.developers.md b/docs/README.developers.md index 3aeaf7c7..a5678f9b 100644 --- a/docs/README.developers.md +++ b/docs/README.developers.md @@ -4,20 +4,19 @@ We provide a C, C++, Python and .NET CLR interface to the adapter. ## C++ developers * the API can be found in `include/cec.h` -* an example implementation can be found on https://github.com/Pulse-Eight/cec-utils/blob/master/src/cec-client/cec-client.cpp +* an example implementation can be found on https://github.com/Pulse-Eight/libcec/blob/master/src/cec-client/cec-client.cpp ## C developers * the API can be found in `include/cecc.h` -* an example implementation can be found on https://github.com/Pulse-Eight/cec-utils/blob/master/src/cecc-client/cecc-client.cpp +* an example implementation can be found on https://github.com/Pulse-Eight/libcec/blob/master/src/cecc-client/cecc-client.c ## .NET developers * add a reference to `LibCecSharp.dll` * add `cec.dll` to your project and enable "copy to output directory" -* an example implementation can be found on https://github.com/Pulse-Eight/cec-utils/blob/master/src/CecSharpTester/CecSharpClient.cs - +* an example implementation can be found on https://github.com/Pulse-Eight/cec-dotnet/blob/master/src/CecSharpTester/CecSharpClient.cs ## Python developers * the API is exported to Python through Swig -* an example implementation can be found on https://github.com/Pulse-Eight/cec-utils/blob/master/src/pyCecClient/pyCecClient.py +* an example implementation can be found on https://github.com/Pulse-Eight/libcec/blob/master/src/pyCecClient/pyCecClient.py # Developers Agreement From f8eb6c43171c9cd93c30b275edb63ccd911a7fa2 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 12 Jul 2017 13:52:23 +0200 Subject: [PATCH 17/93] added: Panasonic media control info to the readme. Thanks @9000h. Issue --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 02373dec..5263e150 100644 --- a/README.md +++ b/README.md @@ -28,3 +28,8 @@ See [docs/README.windows.md](docs/README.windows.md). # Developers See [docs/README.developers.md](docs/README.developers.md). + +# Vendor specific notes + +## Panasonic +* On Panasonic to enable media control buttons on bottom of the remote, you may have to change the operation mode. To change it, press bottom Power-button, keep it pressed, and press 7 3 Stop. After releasing Power-button, Play, Pause, etc should work in XBMC. From 1a8dc6c96a1c74d2820fea1a39f4dc8057905102 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 12 Jul 2017 15:17:23 +0200 Subject: [PATCH 18/93] fixed: LG - don't activate the source when receiving vendor command 0xB issue #344 --- src/libcec/implementations/SLCommandHandler.cpp | 9 +++++---- src/libcec/implementations/SLCommandHandler.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libcec/implementations/SLCommandHandler.cpp b/src/libcec/implementations/SLCommandHandler.cpp index 386ff398..51487047 100644 --- a/src/libcec/implementations/SLCommandHandler.cpp +++ b/src/libcec/implementations/SLCommandHandler.cpp @@ -117,7 +117,7 @@ int CSLCommandHandler::HandleVendorCommand(const cec_command &command) else if (command.parameters.size == 2 && command.parameters[0] == SL_COMMAND_POWER_ON) { - HandleVendorCommandPowerOn(command); + HandleVendorCommandPowerOn(command, true); return COMMAND_HANDLED; } else if (command.parameters.size == 2 && @@ -129,7 +129,7 @@ int CSLCommandHandler::HandleVendorCommand(const cec_command &command) else if (command.parameters.size == 1 && command.parameters[0] == SL_COMMAND_REQUEST_RECONNECT) { - HandleVendorCommandPowerOn(command); + HandleVendorCommandPowerOn(command, false); return COMMAND_HANDLED; } else if (command.parameters.size == 1 && @@ -168,7 +168,7 @@ void CSLCommandHandler::TransmitVendorCommandSLAckInit(const cec_logical_address SetSLInitialised(); } -void CSLCommandHandler::HandleVendorCommandPowerOn(const cec_command &command) +void CSLCommandHandler::HandleVendorCommandPowerOn(const cec_command &command, bool activateSource) { if (command.initiator != CECDEVICE_TV) return; @@ -176,6 +176,7 @@ void CSLCommandHandler::HandleVendorCommandPowerOn(const cec_command &command) CCECBusDevice *device = m_processor->GetPrimaryDevice(); if (device) { + bool wasActive = device->IsActiveSource(); SetSLInitialised(); device->MarkAsActiveSource(); device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON); @@ -186,7 +187,7 @@ void CSLCommandHandler::HandleVendorCommandPowerOn(const cec_command &command) device->TransmitPowerState(command.initiator, false); device->TransmitPhysicalAddress(false); - if (device->IsActiveSource()) + if (!wasActive || activateSource) ActivateSource(); } } diff --git a/src/libcec/implementations/SLCommandHandler.h b/src/libcec/implementations/SLCommandHandler.h index 27fb1722..a343157b 100644 --- a/src/libcec/implementations/SLCommandHandler.h +++ b/src/libcec/implementations/SLCommandHandler.h @@ -56,7 +56,7 @@ namespace CEC void HandleVendorCommandSLInit(const cec_command &command); void TransmitVendorCommandSLAckInit(const cec_logical_address iSource, const cec_logical_address iDestination); - void HandleVendorCommandPowerOn(const cec_command &command); + void HandleVendorCommandPowerOn(const cec_command &command, bool activateSource); void HandleVendorCommandPowerOnStatus(const cec_command &command); void HandleVendorCommandSLConnect(const cec_command &command); From 040675eecfb63acf05f63fb24d530d1e3d2dd30b Mon Sep 17 00:00:00 2001 From: Rudi Date: Wed, 12 Jul 2017 15:53:32 +0200 Subject: [PATCH 19/93] Fix menu language string Signed-off-by: Rudi --- src/libcec/CECClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/CECClient.cpp b/src/libcec/CECClient.cpp index b777faae..bb8b0cd9 100644 --- a/src/libcec/CECClient.cpp +++ b/src/libcec/CECClient.cpp @@ -133,7 +133,7 @@ bool CCECClient::OnRegister(void) (*it)->SetOSDName(m_configuration.strDeviceName); // set the default menu language for devices we control - (*it)->SetMenuLanguage(m_configuration.strDeviceLanguage); + (*it)->SetMenuLanguage(std::string(m_configuration.strDeviceLanguage, 3)); } // set the physical address From 456c7a62ad82f43f630a3e1224afb47f6d99082c Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Thu, 13 Jul 2017 12:56:01 +0200 Subject: [PATCH 20/93] added: -std=c++11 to pkg-config ref https://github.com/Pulse-Eight/libcec/issues/266#issuecomment-260001553 --- src/libcec/libcec.pc.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/libcec.pc.in b/src/libcec/libcec.pc.in index 2b9f77ac..00de792b 100644 --- a/src/libcec/libcec.pc.in +++ b/src/libcec/libcec.pc.in @@ -9,4 +9,4 @@ URL: http://www.pulse-eight.com/ Version: @LIBCEC_VERSION_MAJOR@.@LIBCEC_VERSION_MINOR@.@LIBCEC_VERSION_PATCH@ Requires: @LIBCEC_LIBREQUIRES@ Libs: -L${libdir} -lcec -Cflags: -I${includedir} -I${includedir}/libcec +Cflags: -I${includedir} -I${includedir}/libcec -std=c++11 From f2c4ca7702d5ae0301c9648fee7cf5525b4e11db Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Thu, 13 Jul 2017 13:29:55 +0200 Subject: [PATCH 21/93] added: instructions for hdmi_force_hotplug=1 on the pi --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 5263e150..6a584f09 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,6 @@ See [docs/README.developers.md](docs/README.developers.md). ## Panasonic * On Panasonic to enable media control buttons on bottom of the remote, you may have to change the operation mode. To change it, press bottom Power-button, keep it pressed, and press 7 3 Stop. After releasing Power-button, Play, Pause, etc should work in XBMC. + +## Raspberry Pi +* If your TV cannot detect the Raspberry Pi's CEC, or if the the Pi can't detect the TV, try adding the following line in `/boot/config.txt` and reboot the Pi: `hdmi_force_hotplug=1` From 5b4faecdeb1d6f8b27822fc50f4b49f6f5c66b95 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Tue, 18 Jul 2017 18:27:13 +0200 Subject: [PATCH 22/93] fixed: correct python lib path for python 2.7+/3+. issue #356 --- src/libcec/cmake/CheckPlatformSupport.cmake | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index 19cb8569..73612dec 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -189,9 +189,18 @@ else() swig_link_libraries(cec ${PYTHON_LIBRARIES}) swig_link_libraries(cec cec) + SET(PYTHON_LIB_INSTALL_PATH "/cec" CACHE STRING "python lib path") + if (${CMAKE_MAJOR_VERSION} GREATER 2 AND ${CMAKE_MAJOR_VERSION} GREATER_EQUAL 7) + SET(PYTHON_LIB_INSTALL_PATH "" CACHE STRING "python lib path" FORCE) + else() + if (${CMAKE_MAJOR_VERSION} GREATER_EQUAL 3) + SET(PYTHON_LIB_INSTALL_PATH "" CACHE STRING "python lib path" FORCE) + endif() + endif() + if(WIN32) install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} - DESTINATION python/cec) + DESTINATION python/${PYTHON_LIB_INSTALL_PATH}) install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cec.py DESTINATION python/cec RENAME __init__.py) @@ -209,7 +218,7 @@ else() endif() install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} - DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}/cec) + DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}/${PYTHON_LIB_INSTALL_PATH}) install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cec.py DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}/cec RENAME __init__.py) From 8adc786bac9234fc298c941dd442c3af3155a522 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Tue, 18 Jul 2017 18:28:58 +0200 Subject: [PATCH 23/93] updated installer for new python paths --- project/libCEC.nsi | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/project/libCEC.nsi b/project/libCEC.nsi index 9fb11800..b214bb8f 100644 --- a/project/libCEC.nsi +++ b/project/libCEC.nsi @@ -200,9 +200,9 @@ Section "Python bindings" SecPythonCec ; Copy to the installation directory SetOutPath "$INSTDIR\python" File "..\build\x86\python\pyCecClient.py" + File "..\build\x86\python\_cec.pyd" SetOutPath "$INSTDIR\python\cec" File "..\build\x86\python\cec\__init__.py" - File "..\build\x86\python\cec\_cec.pyd" SectionEnd !define EVENTGHOST_SECTIONNAME "EventGhost plugin" @@ -212,9 +212,10 @@ Section "" SecEvGhostCec ${If} $EventGhostLocation != "" SetOutPath "$EventGhostLocation\plugins\libCEC\cec" + File "..\build\x86\python\cec\__init__.py" + SetOutPath "$EventGhostLocation\plugins\libCEC" File "..\build\x86\cec.dll" - File "..\build\x86\python\cec\__init__.py" - File "..\build\x86\python\cec\_cec.pyd" + File "..\build\x86\python\_cec.pyd" SetOutPath "$EventGhostLocation\plugins\libCEC" File "..\src\EventGhost\__init__.py" From b4bd513ef7299720f6dfc21970a85d6a7a658a05 Mon Sep 17 00:00:00 2001 From: Kevin Schlosser Date: Wed, 16 Aug 2017 01:13:32 -0600 Subject: [PATCH 24/93] Modifies nsis script to call eventghost to install the plugin --- project/libCEC.nsi | 54 ++- src/EventGhost/PulseEight-1.1b.egplugin | Bin 0 -> 54172 bytes src/EventGhost/__init__.py | 342 ------------------- src/EventGhost/cec.png | Bin 40711 -> 0 bytes src/EventGhost/libCEC_Demo_Configuration.xml | 245 ------------- 5 files changed, 44 insertions(+), 597 deletions(-) create mode 100644 src/EventGhost/PulseEight-1.1b.egplugin delete mode 100644 src/EventGhost/__init__.py delete mode 100644 src/EventGhost/cec.png delete mode 100644 src/EventGhost/libCEC_Demo_Configuration.xml diff --git a/project/libCEC.nsi b/project/libCEC.nsi index b214bb8f..c4c810f0 100644 --- a/project/libCEC.nsi +++ b/project/libCEC.nsi @@ -205,24 +205,57 @@ Section "Python bindings" SecPythonCec File "..\build\x86\python\cec\__init__.py" SectionEnd +; Function used to get the parent directory of the installer +Function GetParentDirectory + + Exch $R0 + Push $R1 + Push $R2 + Push $R3 + + StrCpy $R1 0 + StrLen $R2 $R0 + + loop: + IntOp $R1 $R1 + 1 + IntCmp $R1 $R2 get 0 get + StrCpy $R3 $R0 1 -$R1 + StrCmp $R3 "\" get + Goto loop + + get: + StrCpy $R0 $R0 -$R1 + + Pop $R3 + Pop $R2 + Pop $R1 + Exch $R0 + +FunctionEnd + !define EVENTGHOST_SECTIONNAME "EventGhost plugin" Section "" SecEvGhostCec SetShellVarContext current SectionIn 1 3 ${If} $EventGhostLocation != "" - SetOutPath "$EventGhostLocation\plugins\libCEC\cec" + ; We get the directory of the installer then pass it to GetParentDirectory + ; which we then append the path to the plugin file to the returned value + : This is done because EventGhost needs to see the full path to the plugin + ; file. + Push $EXEDIR + Call GetParentDirectory + Pop $R0 + ExecWait '"$EventGhostLocation\eventghost.exe" $R0\src\EventGhost\PulseEight-1.1b.egplugin' + + ; The new .egplugin format writes the plugin to the program data folder + ; instead of to the EventGhost installation directory + SetOutPath "$%PROGRAMDATA%\EventGhost\plugins\PulseEight\cec" File "..\build\x86\python\cec\__init__.py" - SetOutPath "$EventGhostLocation\plugins\libCEC" + SetOutPath "$%PROGRAMDATA%\EventGhost\plugins\PulseEight" File "..\build\x86\cec.dll" File "..\build\x86\python\_cec.pyd" - SetOutPath "$EventGhostLocation\plugins\libCEC" - File "..\src\EventGhost\__init__.py" - File "..\src\EventGhost\cec.png" - - SetOutPath $EventGhostLocation - File "..\src\EventGhost\libCEC_Demo_Configuration.xml" ${EndIf} SectionEnd @@ -329,10 +362,11 @@ Section "Uninstall" ${EndIf} ; Uninstall EventGhost plugin + ; Eventghost has no uninstall plugin feature so we sinply delete the plugin + ; from the directory. ReadRegDword $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\EventGhost_is1" "InstallLocation" ${If} $1 != "" - RMDir /r "$1\plugins\libCEC" - Delete "$1\libCEC_Demo_Configuration.xml" + RMDir /r "$%PROGRAMDATA%\EventGhost\plugins\PulseEight" ${Endif} ; Uninstall the driver diff --git a/src/EventGhost/PulseEight-1.1b.egplugin b/src/EventGhost/PulseEight-1.1b.egplugin new file mode 100644 index 0000000000000000000000000000000000000000..39043e7bd4b6044195d7e334802d2fddf070f6c8 GIT binary patch literal 54172 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 literal 0 HcmV?d00001 diff --git a/src/EventGhost/__init__.py b/src/EventGhost/__init__.py deleted file mode 100644 index f837659b..00000000 --- a/src/EventGhost/__init__.py +++ /dev/null @@ -1,342 +0,0 @@ -# -*- coding: utf-8 -*- -# This file is part of the libCEC(R) library. -# -# libCEC(R) is Copyright (C) 2011-2015 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., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301 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 -# http://www.pulse-eight.com/ -# http://www.pulse-eight.net/ - -description = """ -Integration with libCEC, which adds support for Pulse-Eight's `CEC adapters `_. - -| - -.. image:: cec.png - :align: center - -**Notice:** -Set up the HDMI input to which your adapter is connected correctly, or remote control input won't work. -""" - -import cec - -eg.RegisterPlugin( - name = 'Pulse-Eight CEC adapter', - author = 'Lars Op den Kamp', - version = '0.3', - kind = 'remote', - guid = '{fd322eea-c897-470c-bef7-77bf15c52db4}', - url = 'http://libcec.pulse-eight.com/', - description = description, - createMacrosOnAdd = False, - hardwareId = "USB\\VID_2548&PID_1002", -) - -# logging callback -def log_callback(level, time, message): - return CEC.instance.LogCallback(level, time, message) - -# key press callback -def key_press_callback(key, duration): - return CEC.instance.KeyPressCallback(key, duration) - -class CEC(eg.PluginClass): - instance = {} - logicalAddressNames = [] - lastKeyPressed = 255 - - # detect an adapter and return the com port path - def DetectAdapter(self): - retval = None - adapters = self.lib.DetectAdapters() - for adapter in adapters: - print("found a CEC adapter: " + adapter.strComName) - retval = adapter.strComName - return retval - - # initialise libCEC - def InitLibCec(self, connectedAvr, portNumber, deviceName): - if connectedAvr: - self.cecconfig.baseDevice = cec.CECDEVICE_AUDIOSYSTEM - else: - self.cecconfig.baseDevice = cec.CECDEVICE_TV - self.cecconfig.iHDMIPort = portNumber - self.cecconfig.strDeviceName = str(deviceName) - self.cecconfig.bActivateSource = 0 - - self.lib = cec.ICECAdapter.Create(self.cecconfig) - - # search for adapters - adapter = self.DetectAdapter() - if adapter == None: - print("No adapters found") - else: - if self.lib.Open(adapter): - print("connection opened") - return True - else: - print("failed to open a connection to the CEC adapter") - return False - - def __init__(self): - self.log_level = cec.CEC_LOG_WARNING - self.cecconfig = cec.libcec_configuration() - self.cecconfig.clientVersion = cec.LIBCEC_VERSION_CURRENT - self.cecconfig.deviceTypes.Add(cec.CEC_DEVICE_TYPE_RECORDING_DEVICE) - self.cecconfig.SetLogCallback(log_callback) - self.cecconfig.SetKeyPressCallback(key_press_callback) - CEC.instance = self - self.AddGroup("Actions", ACTIONS) - self.AddActionsFromList(ACTIONS) - self.AddGroup("Queries", QUERIES) - self.AddActionsFromList(QUERIES) - - def __start__(self, connectedAvr, portNumber, deviceName): - self.lastConnectedAvr = connectedAvr - self.lastPortNumber = portNumber - self.lastDeviceName = deviceName - if self.InitLibCec(connectedAvr, portNumber, deviceName): - # print libCEC version and compilation information - print("libCEC version " + self.lib.VersionToString(self.cecconfig.serverVersion) + " loaded: " + self.lib.GetLibInfo()) - self.logicalAddressNames = [ self.lib.LogicalAddressToString(cec.CECDEVICE_TV), - self.lib.LogicalAddressToString(cec.CECDEVICE_RECORDINGDEVICE1), - self.lib.LogicalAddressToString(cec.CECDEVICE_RECORDINGDEVICE2), - self.lib.LogicalAddressToString(cec.CECDEVICE_TUNER1), - self.lib.LogicalAddressToString(cec.CECDEVICE_PLAYBACKDEVICE1), - self.lib.LogicalAddressToString(cec.CECDEVICE_AUDIOSYSTEM), - self.lib.LogicalAddressToString(cec.CECDEVICE_TUNER2), - self.lib.LogicalAddressToString(cec.CECDEVICE_TUNER3), - self.lib.LogicalAddressToString(cec.CECDEVICE_PLAYBACKDEVICE2), - self.lib.LogicalAddressToString(cec.CECDEVICE_RECORDINGDEVICE3), - self.lib.LogicalAddressToString(cec.CECDEVICE_TUNER4), - self.lib.LogicalAddressToString(cec.CECDEVICE_PLAYBACKDEVICE3), - self.lib.LogicalAddressToString(cec.CECDEVICE_RESERVED1), - self.lib.LogicalAddressToString(cec.CECDEVICE_RESERVED2), - self.lib.LogicalAddressToString(cec.CECDEVICE_FREEUSE), - self.lib.LogicalAddressToString(cec.CECDEVICE_BROADCAST), - self.lib.LogicalAddressToString(cec.CECDEVICE_UNKNOWN),] - else: - print("Couldn't initialise libCEC. Please check your configuration.") - - def ReinitLibCec(self): - self.lib.Close() - if self.InitLibCec(self.lastConnectedAvr, self.lastPortNumber, self.lastDeviceName): - print "libCEC reinited sucessfully" - else: - print("Could not reinit libCEC") - - def __stop__(self): - self.lib.Close() - - def Configure(self, connectedAvr = False, portNumber = 1, deviceName = "libCEC"): - panel = eg.ConfigPanel() - spPortNumber = panel.SpinIntCtrl(portNumber, min=1, max=15) - cbConnectedAvr = panel.CheckBox(connectedAvr, "") - bxConnection = panel.BoxedGroup( - "Connection", - ("Connected to AVR", cbConnectedAvr), - ("HDMI port number:", spPortNumber), - ) - panel.sizer.Add(bxConnection, 0, wx.EXPAND) - - txDeviceName = panel.TextCtrl(deviceName) - bxDevice = panel.BoxedGroup( - "Device configuration", - ("Device name", txDeviceName), - ) - panel.sizer.Add(bxDevice, 0, wx.EXPAND) - - while panel.Affirmed(): - panel.SetResult(cbConnectedAvr.GetValue(), spPortNumber.GetValue(), txDeviceName.GetValue()) - - def Transmit(self, txcmd): - self.lib.Transmit(self.lib.CommandFromString(txcmd)) - - # logging callback - def LogCallback(self, level, time, message): - if level > self.log_level: - return 0 - - if level == cec.CEC_LOG_ERROR: - levelstr = "ERROR: " - elif level == cec.CEC_LOG_WARNING: - levelstr = "WARNING: " - elif level == cec.CEC_LOG_NOTICE: - levelstr = "NOTICE: " - elif level == cec.CEC_LOG_TRAFFIC: - levelstr = "TRAFFIC: " - elif level == cec.CEC_LOG_DEBUG: - levelstr = "DEBUG: " - - print("CEC " + levelstr + "[" + str(time) + "] " + message) - return 0 - - # key press callback - def KeyPressCallback(self, key, duration): - if duration == 0 and self.lastKeyPressed != key: - self.lastKeyPressed = key - self.TriggerEnduringEvent(self.lib.UserControlCodeToString(key)) - elif duration > 0 and self.lastKeyPressed == key: - self.lastKeyPressed = 255 - self.EndLastEvent() - elif self.lastKeyPressed != key: - self.lastKeyPressed = 255 - self.TriggerEvent(self.lib.UserControlCodeToString(key)) - return 0 - - def NumberToLogicalAddress(self, value): - if value == 0: - return "cec.CECDEVICE_TV" - elif value == 1: - return "cec.CECDEVICE_RECORDINGDEVICE1" - elif value == 2: - return "cec.CECDEVICE_RECORDINGDEVICE2" - elif value == 3: - return "cec.CECDEVICE_TUNER1" - elif value == 4: - return "cec.CECDEVICE_PLAYBACKDEVICE1" - elif value == 5: - return "cec.CECDEVICE_AUDIOSYSTEM" - elif value == 6: - return "cec.CECDEVICE_TUNER2" - elif value == 7: - return "cec.CECDEVICE_TUNER3" - elif value == 8: - return "cec.CECDEVICE_PLAYBACKDEVICE2" - elif value == 9: - return "cec.CECDEVICE_RECORDINGDEVICE3" - elif value == 10: - return "cec.CECDEVICE_TUNER4" - elif value == 11: - return "cec.CECDEVICE_PLAYBACKDEVICE3" - elif value == 12: - return "cec.CECDEVICE_RESERVED1" - elif value == 13: - return "cec.CECDEVICE_RESERVED2" - elif value == 14: - return "cec.CECDEVICE_FREEUSE" - elif value == 15: - return "cec.CECDEVICE_BROADCAST" - return "cec.CECDEVICE_UNKNOWN" - - def command(self, command): - eval(command) - - def query(self, command): - return eval(command) - -class ActionNoParam(eg.ActionClass): - def __call__(self): - self.plugin.command(self.value) - -class ActionParamString(eg.ActionClass): - def __call__(self, value = ""): - self.plugin.command(self.value.format(str(value))) - - def Configure(self, value = ""): - panel = eg.ConfigPanel() - valueCtrl = panel.TextCtrl(value) - valueBox = panel.BoxedGroup("Enter value", ("Value:", valueCtrl),) - panel.sizer.Add(valueBox, 0, wx.EXPAND) - while panel.Affirmed(): - panel.SetResult(valueCtrl.GetValue(),) - -class ActionParamLogicalAddress(eg.ActionClass): - names = [] - selectedValue = 0 - - def __call__(self, value = "cec.CECDEVICE_UNKNOWN"): - self.plugin.command(self.value.format(value)) - - def Configure(self, value = "cec.CECDEVICE_UNKNOWN"): - self.names = self.plugin.logicalAddressNames - panel = eg.ConfigPanel() - - cbAddresses = wx.ComboBox(panel, -1, choices = self.names) - cbAddresses.SetStringSelection(self.plugin.lib.LogicalAddressToString(cec.CECDEVICE_UNKNOWN)) - - def cbAddressesChanged(event = None): - evtName = cbAddresses.GetValue() - if evtName in self.names: - self.selectedValue = self.names.index(evtName) - if event: - event.Skip() - cbAddressesChanged() - cbAddresses.Bind(wx.EVT_COMBOBOX, cbAddressesChanged) - panel.sizer.Add(cbAddresses) - - while panel.Affirmed(): - panel.SetResult(self.plugin.NumberToLogicalAddress(self.selectedValue),) - -class QueryParamLogicalAddress(eg.ActionClass): - names = [] - selectedValue = 0 - - def __call__(self, value = "cec.CECDEVICE_UNKNOWN"): - return self.plugin.query(self.value.format(value)) - - def Configure(self, value = "cec.CECDEVICE_UNKNOWN"): - self.names = self.plugin.logicalAddressNames - panel = eg.ConfigPanel() - - cbAddresses = wx.ComboBox(panel, -1, choices = self.names) - cbAddresses.SetStringSelection(self.plugin.lib.LogicalAddressToString(cec.CECDEVICE_UNKNOWN)) - - def cbAddressesChanged(event = None): - evtName = cbAddresses.GetValue() - if evtName in self.names: - self.selectedValue = self.names.index(evtName) - if event: - event.Skip() - cbAddressesChanged() - cbAddresses.Bind(wx.EVT_COMBOBOX, cbAddressesChanged) - panel.sizer.Add(cbAddresses) - - while panel.Affirmed(): - panel.SetResult(self.plugin.NumberToLogicalAddress(self.selectedValue),) - -ACTIONS = ( - (ActionNoParam, 'ActiveSource', 'Active source', 'Mark this device as active source, which will turn on the TV and switch it to the correct input', u'self.lib.SetActiveSource()'), - (ActionNoParam, 'InactiveView', 'Inactive view', 'Mark this source as inactive. The result can be different per TV. Most will switch to the previous source', u'self.lib.SetInactiveView()'), - (ActionParamLogicalAddress, 'PowerOn', 'Power on device', 'Power on the given device', u'self.lib.PowerOnDevices({0})'), - (ActionNoParam, 'StandbyAll', 'Standby all devices', 'Send the TV and any other CEC capable device to standby', u'self.lib.StandbyDevices(cec.CECDEVICE_BROADCAST)'), - (ActionParamLogicalAddress, 'Standby', 'Standby device', 'Send the given device to standby (if present)', u'self.lib.StandbyDevices({0})'), - - (ActionNoParam, 'VolumeUp', 'Volume up', 'Send a volume up command to the AVR (if present)', u'self.lib.VolumeUp()'), - (ActionNoParam, 'VolumeDown', 'Volume down', 'Send a volume down command to the AVR (if present)', u'self.lib.VolumeDown()'), - (ActionNoParam, 'ToggleMute', 'Toggle volume mute', 'Send a mute toggle command to the AVR (if present)', u'self.lib.MuteAudio()'), - - (ActionParamString, 'RawCommand', 'Send command', 'Send a raw CEC command', u'self.lib.Transmit(self.lib.CommandFromString(\'{0}\'))'), - (ActionNoParam, 'ReinitLibCec', 'Re-initialize Libcec', 'Useful if the device was powered down', u'self.ReinitLibCec()'), -) - -QUERIES = ( - (QueryParamLogicalAddress, 'GetCecVersion', 'Get device CEC version', 'Request the CEC version', u'self.lib.CecVersionToString(self.lib.GetDeviceCecVersion({0}))'), - (QueryParamLogicalAddress, 'GetMenuLanguage', 'Get device menu language', 'Request the menu language', u'self.lib.GetDeviceMenuLanguage({0})'), - (QueryParamLogicalAddress, 'GetVendorId', 'Get device vendor id', 'Request the vendor id', u'self.lib.VendorIdToString(self.lib.GetDeviceVendorId({0}))'), - (QueryParamLogicalAddress, 'GetPowerStatus', 'Get device power status', 'Request the power status', u'self.lib.PowerStatusToString(self.lib.GetDevicePowerStatus({0}))'), -) diff --git a/src/EventGhost/cec.png b/src/EventGhost/cec.png deleted file mode 100644 index 2b3042bafbab76aa4ea5e0c2ff60299333bd8e5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40711 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

*xaJ&eum%tPR1tuW}!_ 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 - - - - gAKJSwFYBgAAAGxpYkNFQ3EAh3EBLg== - - - - - - - CEC.PowerOn('cec.CECDEVICE_AUDIOSYSTEM') - - - - - CEC.GetPowerStatus('cec.CECDEVICE_AUDIOSYSTEM') - - - EventGhost.PythonCommand(u"eg.result == 'standby'") - - - EventGhost.NewJumpIf(XmlIdLink(6), 0, False) - - - - - CEC.GetPowerStatus('cec.CECDEVICE_AUDIOSYSTEM') - - - EventGhost.PythonCommand(u"eg.result == 'on'") - - - EventGhost.NewJumpIf(XmlIdLink(16), 0, False) - - - - - CEC.Standby('cec.CECDEVICE_AUDIOSYSTEM') - - - - - CEC.VolumeUp() - - - - - CEC.VolumeDown() - - - - - CEC.ToggleMute() - - - - - - - CEC.PowerOn('cec.CECDEVICE_TV') - - - - - CEC.Standby('cec.CECDEVICE_TV') - - - - - CEC.GetPowerStatus('cec.CECDEVICE_TV') - - - EventGhost.PythonCommand(u"eg.result == 'standby'") - - - EventGhost.NewJumpIf(XmlIdLink(19), 0, False) - - - - - CEC.GetPowerStatus('cec.CECDEVICE_TV') - - - EventGhost.PythonCommand(u"eg.result == 'standby'") - - - EventGhost.NewJumpIf(XmlIdLink(21), 0, False) - - - - - - CEC.ActiveSource() - - - - - CEC.InactiveView() - - - - - - - Window.SendKeys(u'{Up}', False) - - - EventGhost.AutoRepeat(0.59999999999999998, 0.29999999999999999, 0.01, 3.0) - - - - - - Window.SendKeys(u'{Down}', False) - - - EventGhost.AutoRepeat(0.59999999999999998, 0.29999999999999999, 0.01, 3.0) - - - - - - Window.SendKeys(u'{Left}', False) - - - EventGhost.AutoRepeat(0.59999999999999998, 0.29999999999999999, 0.01, 3.0) - - - - - - Window.SendKeys(u'{Right}', False) - - - EventGhost.AutoRepeat(0.59999999999999998, 0.29999999999999999, 0.01, 3.0) - - - - - - EventGhost.JumpIfLongPress(1.0, XmlIdLink(25)) - - - Window.SendKeys(u'{Backspace}', False) - - - - - Window.SendKeys(u'{Escape}', False) - - - - - - Window.SendKeys(u'{Enter}', False) - - - - - - Window.SendKeys(u'1', False) - - - - - - Window.SendKeys(u'2', False) - - - - - - Window.SendKeys(u'3', False) - - - - - - Window.SendKeys(u'4', False) - - - - - - Window.SendKeys(u'5', False) - - - - - - Window.SendKeys(u'6', False) - - - - - - Window.SendKeys(u'7', False) - - - - - - Window.SendKeys(u'8', False) - - - - - - Window.SendKeys(u'9', False) - - - - - - Window.SendKeys(u'0', False) - - - - - - Window.SendKeys(u'{F1}', False) - - - - - - Window.SendKeys(u'{F2}', False) - - - - - - Window.SendKeys(u'{F3}', False) - - - - - - Window.SendKeys(u'{F4}', False) - - - - - From c2f05f593b1c1601e19b73f39789097deced54c1 Mon Sep 17 00:00:00 2001 From: Kevin Schlosser Date: Thu, 17 Aug 2017 11:23:29 -0600 Subject: [PATCH 25/93] Removes .egplugin binary file New method of creating the .egplugin file through create_installer.cmd. Power shell is used to do this since windows zip compression is not accessable from a command line. I have also added file cleanup to ensure any fils that have been copied to create the .egplugin file were removed to leave the system in the state it was before the installer was created. I also added checking to make sure the .egplugin file was created. and if it was not then to exit the build process. --- project/libCEC.nsi | 33 +- src/EventGhost/PulseEight-1.1b.egplugin | Bin 54172 -> 0 bytes .../egplugin_sources/PulseEight/__init__.py | 746 ++++++++++++++++++ .../egplugin_sources/PulseEight/cec.png | Bin 0 -> 40711 bytes .../PulseEight/cec_classes.py | 656 +++++++++++++++ .../egplugin_sources/PulseEight/controls.py | 258 ++++++ src/EventGhost/egplugin_sources/info.py | 7 + windows/create-installer.cmd | 29 +- 8 files changed, 1707 insertions(+), 22 deletions(-) delete mode 100644 src/EventGhost/PulseEight-1.1b.egplugin create mode 100644 src/EventGhost/egplugin_sources/PulseEight/__init__.py create mode 100644 src/EventGhost/egplugin_sources/PulseEight/cec.png create mode 100644 src/EventGhost/egplugin_sources/PulseEight/cec_classes.py create mode 100644 src/EventGhost/egplugin_sources/PulseEight/controls.py create mode 100644 src/EventGhost/egplugin_sources/info.py diff --git a/project/libCEC.nsi b/project/libCEC.nsi index c4c810f0..7933edcd 100644 --- a/project/libCEC.nsi +++ b/project/libCEC.nsi @@ -20,17 +20,17 @@ Var EventGhostLocation !define MUI_FINISHPAGE_LINK "Visit http://libcec.pulse-eight.com/ for more information." !define MUI_FINISHPAGE_LINK_LOCATION "http://libcec.pulse-eight.com/" -!define MUI_ABORTWARNING +!define MUI_ABORTWARNING !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "..\COPYING" !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY -!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM" -!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\Pulse-Eight\USB-CEC Adapter sofware" +!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM" +!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\Pulse-Eight\USB-CEC Adapter sofware" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" -!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder +!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH @@ -81,8 +81,8 @@ Section "USB-CEC Driver" SecDriver "" "Uninstall Pulse-Eight USB-CEC Adapter software." WriteINIStr "$SMPROGRAMS\$StartMenuFolder\Visit Pulse-Eight.url" "InternetShortcut" "URL" "http://www.pulse-eight.com/" - !insertmacro MUI_STARTMENU_WRITE_END - + !insertmacro MUI_STARTMENU_WRITE_END + ;add entry to add/remove programs WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ "DisplayName" "Pulse-Eight USB-CEC Adapter software" @@ -160,8 +160,8 @@ Section "CEC Debug Client" SecCecClient "" "$INSTDIR\cec-client.exe" 0 SW_SHOWNORMAL \ "" "Start the CEC Test client." ${EndIf} - !insertmacro MUI_STARTMENU_WRITE_END - + !insertmacro MUI_STARTMENU_WRITE_END + SectionEnd Section "libCEC Tray" SecDotNet @@ -189,8 +189,8 @@ Section "libCEC Tray" SecDotNet "" "$INSTDIR\cec-tray.exe" 0 SW_SHOWNORMAL \ "" "Start libCEC Tray." ${EndIf} - !insertmacro MUI_STARTMENU_WRITE_END - + !insertmacro MUI_STARTMENU_WRITE_END + SectionEnd Section "Python bindings" SecPythonCec @@ -246,16 +246,7 @@ Section "" SecEvGhostCec Push $EXEDIR Call GetParentDirectory Pop $R0 - ExecWait '"$EventGhostLocation\eventghost.exe" $R0\src\EventGhost\PulseEight-1.1b.egplugin' - - ; The new .egplugin format writes the plugin to the program data folder - ; instead of to the EventGhost installation directory - SetOutPath "$%PROGRAMDATA%\EventGhost\plugins\PulseEight\cec" - File "..\build\x86\python\cec\__init__.py" - SetOutPath "$%PROGRAMDATA%\EventGhost\plugins\PulseEight" - File "..\build\x86\cec.dll" - File "..\build\x86\python\_cec.pyd" - + ExecWait '"$EventGhostLocation\eventghost.exe" $R0\src\EventGhost\pulse_eight.egplugin' ${EndIf} SectionEnd @@ -379,7 +370,7 @@ Section "Uninstall" Delete "$INSTDIR\Uninstall.exe" RMDir /r "$INSTDIR" RMDir "$PROGRAMFILES\Pulse-Eight" - + !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder Delete "$SMPROGRAMS\$StartMenuFolder\libCEC Tray.lnk" ${If} ${RunningX64} diff --git a/src/EventGhost/PulseEight-1.1b.egplugin b/src/EventGhost/PulseEight-1.1b.egplugin deleted file mode 100644 index 39043e7bd4b6044195d7e334802d2fddf070f6c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54172 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 diff --git a/src/EventGhost/egplugin_sources/PulseEight/__init__.py b/src/EventGhost/egplugin_sources/PulseEight/__init__.py new file mode 100644 index 00000000..732000ec --- /dev/null +++ b/src/EventGhost/egplugin_sources/PulseEight/__init__.py @@ -0,0 +1,746 @@ +# -*- coding: utf-8 -*- +# This file is part of the libCEC(R) library. +# +# libCEC(R) is Copyright (C) 2011-2015 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., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 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 +# http://www.pulse-eight.com/ +# http://www.pulse-eight.net/ +# +# +# The code contained within this file also falls under the GNU license of +# EventGhost +# +# Copyright © 2005-2016 EventGhost Project +# +# EventGhost is free software: 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. +# +# EventGhost 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 EventGhost. If not, see . + +import eg + + +eg.RegisterPlugin( + name='Pulse-Eight CEC adapter', + author='Lars Op den Kamp, K', + version='1.1b', + kind='remote', + # guid='{fd322eea-c897-470c-bef7-77bf15c52db4}', + guid='{81AC5776-0220-4D2A-B561-DD91F052FF7B}', + url='http://libcec.pulse-eight.com/', + description=( + '' + 'Integration with libCEC, which adds support for Pulse-Eight\'s ' + '`CEC adapters `_.\n\n' + '|\n\n' + '.. image:: cec.png\n\n' + '**Notice:** ' + 'Make sure you select the correct HDMI port number on the device that ' + 'the CEC adapter is connected to, ' + 'or remote control input won\'t work.\n' + ), + createMacrosOnAdd=True, + canMultiLoad=False, + hardwareId="USB\\VID_2548&PID_1002", +) + +from cec_classes import UserControlCodes, CECAdapter, AdapterError # NOQA +from controls import DeviceCtrl, AdapterCtrl, AdapterListCtrl # NOQA +from . import cec # NOQA +import threading # NOQA +import wx # NOQA + + +class Text(eg.TranslatableStrings): + mute_group_lbl = 'Mute' + volume_group_lbl = 'Volume' + power_group_lbl = 'Power' + remote_group_lbl = 'Remote Keys' + volume_lbl = 'Volume:' + command_lbl = 'Command:' + key_lbl = 'Remote Key:' + + class RawCommand: + name = 'Send command to an adapter' + description = 'Send a raw CEC command to an adapter' + + class RestartAdapter: + name = 'Restart Adapter' + description = 'Restarts an adapter.' + + class VolumeUp: + name = 'Volume Up' + description = 'Turns up the volume by one point.' + + class VolumeDown: + name = 'Volume Down' + description = 'Turns down the volume by one point.' + + class GetVolume: + name = 'Get Volume' + description = 'Returns the current volume level.' + + class SetVolume: + name = 'Set Volume' + description = 'Sets the volume level.' + + class GetMute: + name = 'Get Mute' + description = 'Returns the mute state.' + + class ToggleMute: + name = 'Toggle Mute' + description = 'Toggles mute On and Off.' + + class MuteOn: + name = 'Mute On' + description = 'Turns mute on.' + + class MuteOff: + name = 'Mute Off' + description = 'Turns mute off.' + + class PowerOnAll: + name = 'Power On All Devices' + description = 'Powers on all devices on a specific adapter.' + + class StandbyAll: + name = 'Standby All Devices' + description = 'Powers off (standby) all devices in a specific adapter.' + + class StandbyDevice: + name = 'Standby a Device' + description = 'Powers off (standby) a single device.' + + class GetDevicePower: + name = 'Get Device Power' + description = 'Returns the power status of a device.' + + class PowerOnDevice: + name = 'Power On a Device' + description = 'Powers on a single device.' + + class GetDeviceVendor: + name = 'Get Device Vendor' + description = 'Returns the vendor of a device.' + + class GetDeviceMenuLanguage: + name = 'Get Device Menu Language' + description = 'Returns the menu language of a device.' + + class IsActiveSource: + name = 'Is Device Active Source' + description = 'Returns True/False if a device is the active source.' + + class IsDeviceActive: + name = 'Is Device Active' + description = 'Returns True/False if a device is active.' + + class GetDeviceOSDName: + name = 'Get Device OSD Name' + description = 'Returns the OSD text that is display for a device.' + + class SetDeviceActiveSource: + name = 'Set Device as Active Source' + description = 'Sets a device as the active source.' + + class SendRemoteKey: + name = 'Send Remote Key' + description = 'Send a Remote Keypress to a specific device.' + + +class PulseEight(eg.PluginBase): + text = Text + + def __init__(self): + self.adapters = [] + + power_group = self.AddGroup(Text.power_group_lbl) + power_group.AddAction(GetDevicePower) + power_group.AddAction(PowerOnDevice) + power_group.AddAction(StandbyDevice) + power_group.AddAction(PowerOnAll) + power_group.AddAction(StandbyAll) + + volume_group = self.AddGroup(Text.volume_group_lbl) + volume_group.AddAction(GetVolume) + volume_group.AddAction(VolumeUp) + volume_group.AddAction(VolumeDown) + volume_group.AddAction(SetVolume) + + mute_group = self.AddGroup(Text.mute_group_lbl) + mute_group.AddAction(GetMute) + mute_group.AddAction(MuteOn) + mute_group.AddAction(MuteOff) + mute_group.AddAction(ToggleMute) + + self.AddAction(SendRemoteKey) + self.AddAction(SetDeviceActiveSource) + self.AddAction(IsActiveSource) + self.AddAction(IsDeviceActive) + self.AddAction(GetDeviceVendor) + self.AddAction(GetDeviceMenuLanguage) + self.AddAction(GetDeviceOSDName) + self.AddAction(RestartAdapter) + self.AddAction(RawCommand) + + remote_group = self.AddGroup(Text.remote_group_lbl) + remote_group.AddActionsFromList(REMOTE_ACTIONS) + + def __start__(self, *adapters): + + def start_connections(*adptrs): + while self.adapters: + pass + + cec_lib = cec.ICECAdapter.Create(cec.libcec_configuration()) + available_coms = list( + a.strComName for a in cec_lib.DetectAdapters() + ) + cec_lib.Close() + + for item in adptrs: + com_port = item[0] + + if com_port in available_coms: + try: + self.adapters += [CECAdapter(*item)] + except AdapterError: + continue + else: + eg.PrintError( + 'CEC Error: adapter on %s is not found' % com_port + ) + + if not self.adapters: + eg.PrintError('CEC Error: no CEC adapters found') + self.__stop__() + + for items in adapters: + if not isinstance(items, tuple): + eg.PrintError( + 'You cannot upgrade to this version.\n' + 'Delete the plugin from the plugins folder ' + 'and then install this one' + ) + break + else: + threading.Thread(target=start_connections, args=adapters).start() + + @eg.LogIt + def __stop__(self): + for adapter in self.adapters: + adapter.close() + + del self.adapters[:] + + def Configure(self, *adapters): + panel = eg.ConfigPanel() + + loading_st = panel.StaticText( + 'Populating CEC Adapters, Please Wait.....' + ) + list_ctrl = AdapterListCtrl(panel) + desc_st = panel.StaticText( + 'Click on "ENTER NAME" and enter a name ' + 'to register an adapter\n' + 'To remove an adapter registration delete the adapter name.' + ) + + ok_button = panel.dialog.buttonRow.okButton + cancel_button = panel.dialog.buttonRow.cancelButton + apply_button = panel.dialog.buttonRow.applyButton + + ok_button.Enable(False) + cancel_button.Enable(False) + apply_button.Enable(False) + + def populate(): + def on_close(_): + pass + + panel.dialog.Bind(wx.EVT_CLOSE, on_close) + + cec_lib = cec.ICECAdapter.Create(cec.libcec_configuration()) + m_adapters = () + + for adapter in cec_lib.DetectAdapters(): + com = adapter.strComName + for settings in adapters: + com_port, adapter_name = settings[:2] + hdmi_port, use_avr, poll_interval = settings[2:] + if com_port == com: + m_adapters += (( + com_port, + adapter_name, + hdmi_port, + use_avr, + poll_interval, + True + ),) + wx.CallAfter(list_ctrl.add_cec_item, *m_adapters[-1]) + break + else: + wx.CallAfter( + list_ctrl.add_cec_item, + com, + 'ENTER NAME', + 1, + False, + 0.5, + None + ) + + for adapter in adapters: + for m_adapter in m_adapters: + if m_adapter[:-1] == adapter: + break + else: + m_adapters += (adapter + (False,),) + wx.CallAfter(list_ctrl.add_cec_item, *m_adapters[-1]) + + cec_lib.Close() + ok_button.Enable(True) + cancel_button.Enable(True) + apply_button.Enable(True) + + panel.dialog.Bind(wx.EVT_CLOSE, panel.dialog.OnCancel) + loading_st.SetLabel('') + + loading_sizer = wx.BoxSizer(wx.HORIZONTAL) + loading_sizer.AddStretchSpacer() + loading_sizer.Add(loading_st, 0, wx.ALL | 5) + loading_sizer.AddStretchSpacer() + + panel.sizer.Add(loading_sizer, 0, wx.EXPAND) + panel.sizer.Add(list_ctrl, 1, wx.EXPAND) + panel.sizer.Add(desc_st, 0, wx.EXPAND) + + threading.Thread(target=populate).start() + + while panel.Affirmed(): + panel.SetResult(*list_ctrl.GetValue()) + + +class AdapterBase(eg.ActionBase): + + def GetLabel(self, com_port=None, adapter_name=None, *_): + return '%s: %s on %s' % (self.name, adapter_name, com_port) + + def _find_adapter(self, com_port, adapter_name): + if com_port is None and adapter_name is None: + return None + + for adapter in self.plugin.adapters: + if com_port == adapter.com_port and adapter_name == adapter.name: + return adapter + if com_port == adapter.com_port: + return adapter + if adapter_name == adapter.name: + return adapter + + def __call__(self, *args): + raise NotImplementedError + + def Configure(self, com_port='', adapter_name=''): + panel = eg.ConfigPanel() + + adapter_ctrl = AdapterCtrl( + panel, + com_port, + adapter_name, + self.plugin.adapters + ) + + panel.sizer.Add(adapter_ctrl, 0, wx.EXPAND) + + while panel.Affirmed(): + panel.SetResult(*adapter_ctrl.GetValue()) + + +class DeviceBase(AdapterBase): + def _process_call(self, device): + raise NotImplementedError + + def __call__(self, com_port=None, adapter_name=None, device='TV'): + adapter = self._find_adapter(com_port, adapter_name) + + if adapter is None: + eg.PrintNotice( + 'CEC: Adapter %s on com port %s not found' % + (adapter_name, com_port) + ) + else: + d = getattr(adapter, device.lower().replace(' ', ''), None) + if d is None: + eg.PrintNotice( + 'CEC: Device %s not found in adapter %s' % + (device, adpater.name) + ) + else: + return self._process_call(d) + + def Configure(self, com_port='', adapter_name='', device='TV'): + panel = eg.ConfigPanel() + + adapter_ctrl = AdapterCtrl( + panel, + com_port, + adapter_name, + self.plugin.adapters + ) + + device_ctrl = DeviceCtrl(panel, device) + if com_port and adapter_name: + device_ctrl.UpdateDevices( + self._find_adapter(com_port, adapter_name) + ) + + def on_choice(evt): + device_ctrl.UpdateDevices( + self._find_adapter(*adapter_ctrl.GetValue()) + ) + + evt.Skip() + + device_ctrl.UpdateDevices( + self._find_adapter(*adapter_ctrl.GetValue()) + ) + + adapter_ctrl.Bind(wx.EVT_CHOICE, on_choice) + panel.sizer.Add(adapter_ctrl, 0, wx.EXPAND) + panel.sizer.Add(device_ctrl, 0, wx.EXPAND) + + while panel.Affirmed(): + com_port, adapter_name = adapter_ctrl.GetValue() + panel.SetResult( + com_port, + adapter_name, + device_ctrl.GetValue() + ) + + +class RestartAdapter(AdapterBase): + + def __call__(self, com_port=None, adapter_name=None): + adapter = self._find_adapter(com_port, adapter_name) + self.plugin.adapters[self.plugin.adapters.index(adapter)] = ( + adapter.restart() + ) + + +class VolumeUp(AdapterBase): + + def __call__(self, com_port=None, adapter_name=None): + adapter = self._find_adapter(com_port, adapter_name) + return adapter.volume_up() + + +class VolumeDown(AdapterBase): + + def __call__(self, com_port=None, adapter_name=None): + adapter = self._find_adapter(com_port, adapter_name) + return adapter.volume_down() + + +class GetVolume(AdapterBase): + + def __call__(self, com_port=None, adapter_name=None): + adapter = self._find_adapter(com_port, adapter_name) + return adapter.volume + + +class GetMute(AdapterBase): + def __call__(self, com_port=None, adapter_name=None): + adapter = self._find_adapter(com_port, adapter_name) + return adapter.mute + + +class ToggleMute(AdapterBase): + def __call__(self, com_port=None, adapter_name=None): + adapter = self._find_adapter(com_port, adapter_name) + return adapter.toggle_mute() + + +class MuteOn(AdapterBase): + def __call__(self, com_port=None, adapter_name=None): + adapter = self._find_adapter(com_port, adapter_name) + adapter.mute = True + return adapter.mute + + +class MuteOff(AdapterBase): + def __call__(self, com_port=None, adapter_name=None): + adapter = self._find_adapter(com_port, adapter_name) + adapter.mute = False + return adapter.mute + + +class PowerOnAll(AdapterBase): + def __call__(self, com_port=None, adapter_name=None): + adapter = self._find_adapter(com_port, adapter_name) + for d in adapter.devices: + d.power = True + + +class StandbyAll(AdapterBase): + def __call__(self, com_port=None, adapter_name=None): + adapter = self._find_adapter(com_port, adapter_name) + for d in adapter.devices: + d.power = False + + +class StandbyDevice(DeviceBase): + def _process_call(self, device): + device.power = False + return device.power + + +class GetDevicePower(DeviceBase): + def _process_call(self, device): + return device.power + + +class PowerOnDevice(DeviceBase): + def _process_call(self, device): + device.power = True + return device.power + + +class GetDeviceVendor(DeviceBase): + def _process_call(self, device): + return device.vendor + + +class GetDeviceMenuLanguage(DeviceBase): + def _process_call(self, device): + return device.menu_language + + +class IsActiveSource(DeviceBase): + def _process_call(self, device): + return device.active_source + + +class IsDeviceActive(DeviceBase): + def _process_call(self, device): + return device.active_device + + +class GetDeviceOSDName(DeviceBase): + def _process_call(self, device): + return device.osd_name + + +class SetDeviceActiveSource(DeviceBase): + def _process_call(self, device): + device.active_source = True + return device.active_source + + +class RawCommand(AdapterBase): + def __call__(self, com_port=None, adapter_name=None, command=""): + adapter = self._find_adapter(com_port, adapter_name) + return adapter.transmit_command(command) + + def Configure(self, com_port='', adapter_name='', command=''): + panel = eg.ConfigPanel() + + adapter_ctrl = AdapterCtrl( + panel, + com_port, + adapter_name, + self.plugin.adapters + ) + + command_st = panel.StaticText(Text.command_lbl) + command_ctrl = panel.TextCtrl(command) + + command_sizer = wx.BoxSizer(wx.HORIZONTAL) + command_sizer.Add(command_st, 0, wx.EXPAND | wx.ALL, 5) + command_sizer.Add(command_ctrl, 0, wx.EXPAND | wx.ALL, 5) + + panel.sizer.Add(adapter_ctrl, 0, wx.EXPAND) + panel.sizer.Add(command_sizer, 0, wx.EXPAND) + + while panel.Affirmed(): + com_port, adapter_name = adapter_ctrl.GetValue() + panel.SetResult(com_port, adapter_name, command_ctrl.GetValue()) + + +class SetVolume(AdapterBase): + + def __call__(self, com_port=None, adapter_name=None, volume=0): + adapter = self._find_adapter(com_port, adapter_name) + adapter.volume = volume + return adapter.volume + + def Configure(self, com_port='', adapter_name='', volume=0): + panel = eg.ConfigPanel() + + adapter_ctrl = AdapterCtrl( + panel, + com_port, + adapter_name, + self.plugin.adapters + ) + volume_st = panel.StaticText(Text.volume_lbl) + volume_ctrl = panel.SpinIntCtrl(volume, min=0, max=100) + sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer.Add(volume_st, 0, wx.EXPAND | wx.ALL, 5) + sizer.Add(volume_ctrl, 0, wx.EXPAND | wx.ALL, 5) + + panel.sizer.Add(adapter_ctrl, 0, wx.EXPAND) + panel.sizer.Add(sizer, 0, wx.EXPAND) + + while panel.Affirmed(): + com_port, adapter_name = adapter_ctrl.GetValue() + panel.SetResult(com_port, adapter_name, volume_ctrl.GetValue()) + + +class SendRemoteKey(AdapterBase): + + def __call__( + self, + com_port=None, + adapter_name=None, + device='TV', + key=None + ): + if key is None: + key = getattr(self, 'value', None) + if key is None or (com_port is None and adapter_name is None): + eg.PrintNotice( + 'CEC: This action needs to be configured before use.' + ) + return + + adapter = self._find_adapter(com_port, adapter_name) + + if adapter is None: + eg.PrintNotice( + 'CEC: Adapter %s on com port %s not found' % + (adapter_name, com_port) + ) + else: + d = getattr(adapter, device.lower().replace(' ', ''), None) + if d is None: + eg.PrintNotice( + 'CEC: Device %s not found in adapter %s' % + (device, adpater.name) + ) + else: + remote = getattr(d, key, None) + if remote is None: + eg.PrintError( + 'CEC: Key %s not found for device %s on adapter %s' % + (key, device, adpater.name) + ) + else: + import time + remote.send_key_press() + time.sleep(0.1) + remote.send_key_release() + + def Configure(self, com_port='', adapter_name='', device='TV', key=None): + + panel = eg.ConfigPanel() + + adapter_ctrl = AdapterCtrl( + panel, + com_port, + adapter_name, + self.plugin.adapters + ) + + device_ctrl = DeviceCtrl(panel, device) + + device_ctrl.UpdateDevices( + self._find_adapter(*adapter_ctrl.GetValue()) + ) + + def on_choice(evt): + device_ctrl.UpdateDevices( + self._find_adapter(*adapter_ctrl.GetValue()) + ) + + evt.Skip() + + adapter_ctrl.Bind(wx.EVT_CHOICE, on_choice) + panel.sizer.Add(adapter_ctrl, 0, wx.EXPAND) + panel.sizer.Add(device_ctrl, 0, wx.EXPAND) + + if key is None and not hasattr(self, 'value'): + key = '' + key_st = panel.StaticText(Text.key_lbl) + key_ctrl = panel.Choice( + 0, + choices=list(key_name for key_name in UserControlCodes) + ) + + key_ctrl.SetStringSelection(key) + + key_sizer = wx.BoxSizer(wx.HORIZONTAL) + key_sizer.Add(key_st, 0, wx.EXPAND | wx.ALL, 5) + key_sizer.Add(key_ctrl, 0, wx.EXPAND | wx.ALL, 5) + panel.sizer.Add(key_sizer, 0, wx.EXPAND) + else: + key_ctrl = None + + while panel.Affirmed(): + com_port, adapter_name = adapter_ctrl.GetValue() + panel.SetResult( + com_port, + adapter_name, + device_ctrl.GetValue(), + None if key_ctrl is None else key_ctrl.GetStringSelection() + ) + +REMOTE_ACTIONS = () + +for remote_key in UserControlCodes: + key_func = remote_key + for rep in ('Samsung', 'Blue', 'Red', 'Green', 'Yellow'): + key_func = key_func.replace(' (%s)' % rep, '') + key_func = key_func.replace('.', 'DOT').replace('+', '_').replace(' ', '_') + + REMOTE_ACTIONS += (( + SendRemoteKey, + 'fn' + key_func.upper(), + 'Remote Key: ' + remote_key, + 'Remote Key ' + remote_key, + remote_key + ),) diff --git a/src/EventGhost/egplugin_sources/PulseEight/cec.png b/src/EventGhost/egplugin_sources/PulseEight/cec.png new file mode 100644 index 0000000000000000000000000000000000000000..2b3042bafbab76aa4ea5e0c2ff60299333bd8e5d GIT binary patch literal 40711 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

*xaJ&eum%tPR1tuW}!_ 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 +# http://www.pulse-eight.com/ +# http://www.pulse-eight.net/ +# +# +# The code contained within this file also falls under the GNU license of +# EventGhost +# +# Copyright © 2005-2016 EventGhost Project +# +# EventGhost is free software: 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. +# +# EventGhost 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 EventGhost. If not, see . + +from . import cec +import threading +import eg + +CEC_LOG_CONSTANTS = { + cec.CEC_LOG_ERROR: "ERROR: ", + cec.CEC_LOG_WARNING: "WARNING: ", + cec.CEC_LOG_NOTICE: "NOTICE: ", + cec.CEC_LOG_TRAFFIC: "TRAFFIC: ", + cec.CEC_LOG_DEBUG: "DEBUG: ", + cec.CEC_LOG_ALL: "ALL: " +} + +CEC_POWER_CONSTANTS = { + cec.CEC_POWER_STATUS_ON: True, + cec.CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY: False, + cec.CEC_POWER_STATUS_STANDBY: False, + cec.CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON: True, + cec.CEC_POWER_STATUS_UNKNOWN: None +} + +_CONTROL_CODES = [ + cec.CEC_USER_CONTROL_CODE_SELECT, + cec.CEC_USER_CONTROL_CODE_UP, + cec.CEC_USER_CONTROL_CODE_DOWN, + cec.CEC_USER_CONTROL_CODE_LEFT, + cec.CEC_USER_CONTROL_CODE_RIGHT, + cec.CEC_USER_CONTROL_CODE_RIGHT_UP, + cec.CEC_USER_CONTROL_CODE_RIGHT_DOWN, + cec.CEC_USER_CONTROL_CODE_LEFT_UP, + cec.CEC_USER_CONTROL_CODE_LEFT_DOWN, + cec.CEC_USER_CONTROL_CODE_ROOT_MENU, + cec.CEC_USER_CONTROL_CODE_SETUP_MENU, + cec.CEC_USER_CONTROL_CODE_CONTENTS_MENU, + cec.CEC_USER_CONTROL_CODE_FAVORITE_MENU, + cec.CEC_USER_CONTROL_CODE_EXIT, + cec.CEC_USER_CONTROL_CODE_TOP_MENU, + cec.CEC_USER_CONTROL_CODE_DVD_MENU, + cec.CEC_USER_CONTROL_CODE_NUMBER_ENTRY_MODE, + cec.CEC_USER_CONTROL_CODE_NUMBER11, + cec.CEC_USER_CONTROL_CODE_NUMBER12, + cec.CEC_USER_CONTROL_CODE_NUMBER0, + cec.CEC_USER_CONTROL_CODE_NUMBER1, + cec.CEC_USER_CONTROL_CODE_NUMBER2, + cec.CEC_USER_CONTROL_CODE_NUMBER3, + cec.CEC_USER_CONTROL_CODE_NUMBER4, + cec.CEC_USER_CONTROL_CODE_NUMBER5, + cec.CEC_USER_CONTROL_CODE_NUMBER6, + cec.CEC_USER_CONTROL_CODE_NUMBER7, + cec.CEC_USER_CONTROL_CODE_NUMBER8, + cec.CEC_USER_CONTROL_CODE_NUMBER9, + cec.CEC_USER_CONTROL_CODE_DOT, + cec.CEC_USER_CONTROL_CODE_ENTER, + cec.CEC_USER_CONTROL_CODE_CLEAR, + cec.CEC_USER_CONTROL_CODE_NEXT_FAVORITE, + cec.CEC_USER_CONTROL_CODE_CHANNEL_UP, + cec.CEC_USER_CONTROL_CODE_CHANNEL_DOWN, + cec.CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL, + cec.CEC_USER_CONTROL_CODE_SOUND_SELECT, + cec.CEC_USER_CONTROL_CODE_INPUT_SELECT, + cec.CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION, + cec.CEC_USER_CONTROL_CODE_HELP, + cec.CEC_USER_CONTROL_CODE_PAGE_UP, + cec.CEC_USER_CONTROL_CODE_PAGE_DOWN, + cec.CEC_USER_CONTROL_CODE_POWER, + cec.CEC_USER_CONTROL_CODE_VOLUME_UP, + cec.CEC_USER_CONTROL_CODE_VOLUME_DOWN, + cec.CEC_USER_CONTROL_CODE_MUTE, + cec.CEC_USER_CONTROL_CODE_PLAY, + cec.CEC_USER_CONTROL_CODE_STOP, + cec.CEC_USER_CONTROL_CODE_PAUSE, + cec.CEC_USER_CONTROL_CODE_RECORD, + cec.CEC_USER_CONTROL_CODE_REWIND, + cec.CEC_USER_CONTROL_CODE_FAST_FORWARD, + cec.CEC_USER_CONTROL_CODE_EJECT, + cec.CEC_USER_CONTROL_CODE_FORWARD, + cec.CEC_USER_CONTROL_CODE_BACKWARD, + cec.CEC_USER_CONTROL_CODE_STOP_RECORD, + cec.CEC_USER_CONTROL_CODE_PAUSE_RECORD, + cec.CEC_USER_CONTROL_CODE_ANGLE, + cec.CEC_USER_CONTROL_CODE_SUB_PICTURE, + cec.CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND, + cec.CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE, + cec.CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING, + cec.CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION, + cec.CEC_USER_CONTROL_CODE_SELECT_BROADCAST_TYPE, + cec.CEC_USER_CONTROL_CODE_SELECT_SOUND_PRESENTATION, + cec.CEC_USER_CONTROL_CODE_PLAY_FUNCTION, + cec.CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION, + cec.CEC_USER_CONTROL_CODE_RECORD_FUNCTION, + cec.CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION, + cec.CEC_USER_CONTROL_CODE_STOP_FUNCTION, + cec.CEC_USER_CONTROL_CODE_MUTE_FUNCTION, + cec.CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION, + cec.CEC_USER_CONTROL_CODE_TUNE_FUNCTION, + cec.CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION, + cec.CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION, + cec.CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION, + cec.CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION, + cec.CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION, + cec.CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION, + cec.CEC_USER_CONTROL_CODE_F1_BLUE, + cec.CEC_USER_CONTROL_CODE_F2_RED, + cec.CEC_USER_CONTROL_CODE_F3_GREEN, + cec.CEC_USER_CONTROL_CODE_F4_YELLOW, + cec.CEC_USER_CONTROL_CODE_F5, + cec.CEC_USER_CONTROL_CODE_DATA, + cec.CEC_USER_CONTROL_CODE_AN_RETURN, + cec.CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST, + cec.CEC_USER_CONTROL_CODE_MAX, + cec.CEC_USER_CONTROL_CODE_UNKNOWN, +] + + +class _UserControlCodes(object): + _control_codes = {} + + def __init__(self): + cec_lib = cec.ICECAdapter.Create(cec.libcec_configuration()) + + for code in _CONTROL_CODES: + code_name = cec_lib.UserControlCodeToString(code).title() + self._control_codes[code_name.replace(' (Function)', '')] = code + cec_lib.Close() + + def __iter__(self): + for key in sorted(self._control_codes.keys()): + yield key + + def __contains__(self, item): + return item in self._control_codes + + def __getattr__(self, item): + if item in self.__dict__: + return self.__dict__[item] + + if item in self._control_codes: + return self._control_codes[item] + + for key in self._control_codes: + if '(%s)' % item in key: + return self._control_codes[key] + + raise AttributeError + + +UserControlCodes = _UserControlCodes() + + +class CECDevice(object): + def __init__(self, adapter, name, device_const): + self.adapter = adapter + self.sla = adapter.LogicalAddressToString(device_const) + self.pa = adapter.GetDevicePhysicalAddress(device_const) + self.la = device_const + self._osd_event = threading.Event() + self._osd_thread = None + self._osd_string = None + self.name = name + + @property + def osd_name(self): + return self.adapter.GetDeviceOSDName(self.la) + + @property + def osd_string(self): + return self._osd_string + + @osd_string.setter + def osd_string(self, (msg, duration)): + if self._osd_thread is not None: + self._osd_event.set() + self._osd_thread.join(1.0) + + self._osd_event.clear() + + def clear_osd(): + self._osd_event.wait(duration) + self._osd_string = None + self._osd_thread = None + + self._osd_string = msg + self._osd_thread = threading.Thread(target=clear_osd()) + self.adapter.SetOSDString(self.la, duration, msg) + self._osd_thread.start() + + @property + def menu_language(self): + return self.adapter.GetDeviceMenuLanguage(self.la) + + @property + def cec_version(self): + cec_version = self.adapter.GetDeviceCecVersion(self.la) + return self.adapter.CecVersionToString(cec_version) + + @property + def vendor(self): + vendor_id = self.adapter.GetDeviceVendorId(self.la) + return self.adapter.VendorIdToString(vendor_id) + + @property + def power(self): + return CEC_POWER_CONSTANTS[self.adapter.GetDevicePowerStatus(self.la)] + + @power.setter + def power(self, flag): + if flag: + self.adapter.PowerOnDevices(self.la) + else: + self.adapter.StandbyDevices(self.la) + + @property + def active_device(self): + return self.adapter.IsActiveDevice(self.la) + + @property + def active_source(self): + return self.adapter.IsActiveSource(self.la) + + @active_source.setter + def active_source(self, flag=True): + if flag: + self.adapter.SetActiveSource(self.la) + + def __getattr__(self, item): + if item in self.__dict__: + return self.__dict__[item] + + if item in UserControlCodes: + adapter = self.adapter + + class Wrapper: + def __init__(self): + pass + + @staticmethod + def send_key_press(): + code = getattr(UserControlCodes, item) + print code + adapter.SendKeypress( + self.la, + code + ) + + @staticmethod + def send_key_release(): + adapter.SendKeyRelease(self.la) + return Wrapper + return None + + +class AdapterError(Exception): + pass + + +class CECAdapter(object): + @eg.LogIt + def __init__(self, com_port, adapter_name, hdmi_port, use_avr, poll_interval): + self.name = adapter_name + self.com_port = com_port + self._log_level = None + self._menu_state = False + self._key_event = None + self._last_key = 255 + self._restart_params = (com_port, adapter_name, hdmi_port, use_avr) + self._poll_event = threading.Event() + self._poll_interval = poll_interval + self._poll_thread = threading.Thread( + name='PulseEightCEC-' + adapter_name, + target=self._run_poll + ) + + self.cec_config = cec_config = cec.libcec_configuration() + cec_config.clientVersion = cec.LIBCEC_VERSION_CURRENT + cec_config.deviceTypes.Add( + cec.CEC_DEVICE_TYPE_RECORDING_DEVICE + ) + cec_config.SetLogCallback(self._log_callback) + cec_config.SetKeyPressCallback(self._key_callback) + cec_config.iHDMIPort = hdmi_port + cec_config.strDeviceName = str(adapter_name) + cec_config.bActivateSource = 0 + + if use_avr: + cec_config.baseDevice = cec.CECDEVICE_AUDIOSYSTEM + else: + cec_config.baseDevice = cec.CECDEVICE_TV + + self.adapter = adapter = cec.ICECAdapter.Create(cec_config) + + if adapter.Open(com_port): + eg.Print('CEC: connection opened on ' + com_port) + else: + eg.PrintError( + 'CEC Error: connection failed on ' + com_port + ) + raise AdapterError + + self.tv = CECDevice(adapter, 'TV', cec.CECDEVICE_TV) + self.tuner1 = CECDevice(adapter, 'Tuner 1', cec.CECDEVICE_TUNER1) + self.tuner2 = CECDevice(adapter, 'Tuner 2', cec.CECDEVICE_TUNER2) + self.tuner3 = CECDevice(adapter, 'Tuner 3', cec.CECDEVICE_TUNER3) + self.tuner4 = CECDevice(adapter, 'Tuner 4', cec.CECDEVICE_TUNER4) + self.audiosystem = CECDevice(adapter, 'AVR', cec.CECDEVICE_AUDIOSYSTEM) + self.freeuse = CECDevice(adapter, 'Free Use', cec.CECDEVICE_FREEUSE) + self.unknown = CECDevice(adapter, 'Unknown', cec.CECDEVICE_UNKNOWN) + self.broadcast = CECDevice( + adapter, + 'Broadcast', + cec.CECDEVICE_BROADCAST + ) + self.reserved1 = CECDevice( + adapter, + 'Reserved 1', + cec.CECDEVICE_RESERVED1 + ) + self.reserved2 = CECDevice( + adapter, + 'Reserved 2', + cec.CECDEVICE_RESERVED2 + ) + self.recordingdevice1 = CECDevice( + adapter, + 'Recording Device 1', + cec.CECDEVICE_RECORDINGDEVICE1 + ) + self.playbackdevice1 = CECDevice( + adapter, + 'Playback Device 1', + cec.CECDEVICE_PLAYBACKDEVICE1 + ) + self.recordingdevice2 = CECDevice( + adapter, + 'Recording Device 2', + cec.CECDEVICE_RECORDINGDEVICE2 + ) + self.playbackdevice2 = CECDevice( + adapter, + 'Playback Device 2', + cec.CECDEVICE_PLAYBACKDEVICE2 + ) + self.recordingdevice3 = CECDevice( + adapter, + 'Recording Device 3', + cec.CECDEVICE_RECORDINGDEVICE3 + ) + self.playbackdevice3 = CECDevice( + adapter, + 'Playback Device 3', + cec.CECDEVICE_PLAYBACKDEVICE3 + ) + + self.devices = [ + self.tv, + self.audiosystem, + self.tuner1, + self.tuner2, + self.tuner3, + self.tuner4, + self.recordingdevice1, + self.recordingdevice2, + self.recordingdevice3, + self.playbackdevice1, + self.playbackdevice2, + self.playbackdevice3, + self.reserved1, + self.reserved2, + self.freeuse, + self.broadcast, + self.unknown, + ] + self._poll_thread.start() + + def _run_poll(self): + devices = [] + + volume = self.volume + mute = self.mute + menu = self.menu + + for device in self.devices: + try: + devices.append([ + device.active_device, + device.active_source, + device.power, + device.menu_language + ]) + except: + devices.append([None] * 4) + + while not self._poll_event.isSet(): + new_volume = self.volume + new_mute = self.mute + new_menu = self.menu + + if volume != new_volume: + volume = new_volume + if volume is not None: + eg.TriggerEvent( + prefix=self.name, + suffix='Volume.' + str(volume) + ) + + if mute != new_mute: + mute = new_mute + if mute is not None: + if mute: + suffix = 'On' + else: + suffix = 'Off' + + eg.TriggerEvent( + prefix=self.name, + suffix='Mute.' + suffix + ) + + if menu != new_menu: + menu = new_menu + if menu is not None: + if menu: + suffix = 'Opened' + else: + suffix = 'Closed' + + eg.TriggerEvent( + prefix=self.name, + suffix='Menu.' + suffix + ) + + for i, device in enumerate(self.devices): + active, source, power, language = devices[i] + + new_active = device.active_device + new_source = device.active_source + new_power = device.power + new_language = device.menu_language + + if active != new_active: + active = new_active + if active: + suffix = 'Active' + else: + suffix = 'Inactive' + + eg.TriggerEvent( + prefix=self.name, + suffix=device.name + '.' + suffix + ) + + if source != new_source: + source = new_source + if source: + eg.TriggerEvent( + prefix=self.name, + suffix='Source.' + device.name + ) + + if power != new_power: + if power is None: + eg.TriggerEvent( + prefix=self.name, + suffix=device.name + '.Connected' + ) + power = new_power + if power is None: + eg.TriggerEvent( + prefix=self.name, + suffix=device.name + '.Disconnected' + ) + else: + if power: + suffix = 'On' + else: + suffix = 'Off' + eg.TriggerEvent( + prefix=self.name, + suffix=device.name + '.Power.' + suffix + ) + + if language != new_language: + language = new_language + eg.TriggerEvent( + prefix=self.name, + suffix=device.name + '.MenuLanguage.' + str(language) + ) + + devices[i] = [active, source, power, language] + self._poll_event.wait(self._poll_interval) + + def transmit_command(self, command): + return self.adapter.Transmit(self.adapter.CommandFromString(command)) + + def _log_callback(self, level, time, message): + if ( + self._log_level is not None and + level <= self._log_level and + level in CEC_LOG_CONSTANTS + ): + level_str = CEC_LOG_CONSTANTS[level] + eg.PrintDebugNotice( + "CEC %s: %s [%s] %s" % + (self.name, level_str, str(time), message) + ) + return 0 + + def _key_callback(self, key, duration): + str_key = lib.UserControlCodeToString(key).title() + if duration == 0 and self._last_key != key: + self._last_key = key + self._key_event = eg.TriggerEnduringEvent( + prefix=self._name, + suffix='KeyPressed.' + str_key + ) + elif duration > 0 and self._last_key == key: + self._last_key = 255 + self._key_event.SetShouldEnd() + self._key_event = None + elif self._last_key != key: + self._last_key = 255 + eg.TriggerEvent( + prefix=self._name, + suffix='KeyPressed.' + str_key + ) + return 0 + + @property + def log_level(self): + return self._log_level + + @log_level.setter + def log_level(self, level): + if level is not None and level not in CEC_LOG_CONSTANTS: + return + self._log_level = level + + @property + def vendor(self): + vendor_id = self.adapter.GetAdapterVendorId() + return self.adapter.VendorIdToString(vendor_id) + + @property + def menu(self): + return self._menu_state + + @menu.setter + def menu(self, state): + self._menu_state = state + self.adapter.SetMenuState(state) + + def set_interactive_view(self): + self.adapter.SetInactiveView() + + @property + def volume(self): + res = self.adapter.AudioStatus() ^ cec.CEC_AUDIO_MUTE_STATUS_MASK + if res == 255: + return None + return res + + @volume.setter + def volume(self, volume): + if volume < self.volume: + while volume < self.volume: + self.volume_down() + + elif volume > self.volume: + while volume > self.volume: + self.volume_up() + + def volume_up(self): + self.adapter.VolumeUp() + return self.volume + + def volume_down(self): + self.adapter.VolumeDown() + return self.volume + + @property + def mute(self): + return ( + self.adapter.AudioStatus() & cec.CEC_AUDIO_MUTE_STATUS_MASK == + cec.CEC_AUDIO_MUTE_STATUS_MASK + ) + + @mute.setter + def mute(self, flag): + if flag and not self.mute: + self.adapter.AudioMute() + elif not flag and self.mute: + self.adapter.AudioUnmute() + + def toggle_mute(self): + self.adapter.AudioToggleMute() + + def restart(self): + self.close() + return CECAdapter(*self._restart_params) + + def close(self): + self._poll_event.set() + self._poll_thread.join(3) + self.adapter.Close() + eg.Print('CEC: connection closed on ' + self.com_port) diff --git a/src/EventGhost/egplugin_sources/PulseEight/controls.py b/src/EventGhost/egplugin_sources/PulseEight/controls.py new file mode 100644 index 00000000..7dbf2168 --- /dev/null +++ b/src/EventGhost/egplugin_sources/PulseEight/controls.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- +# This file is part of the libCEC(R) library. +# +# libCEC(R) is Copyright (C) 2011-2015 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., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 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 +# http://www.pulse-eight.com/ +# http://www.pulse-eight.net/ +# +# +# The code contained within this file also falls under the GNU license of +# EventGhost +# +# Copyright © 2005-2016 EventGhost Project +# +# EventGhost is free software: 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. +# +# EventGhost 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 EventGhost. If not, see . + +import eg +import threading +import wx +from wx.lib.agw import ultimatelistctrl as ulc + + +class Text(eg.TranslatableStrings): + name_lbl = 'Adapter Name' + avr_lbl = 'AVR Volume Control' + com_port_lbl = 'Adapter COM Port' + hdmi_lbl = 'Device HDMI Port' + adapter_lbl = 'Adapter:' + device_lbl = 'Device:' + poll_lbl = 'Polling Speed (ms)' + + +class AdapterListCtrl(ulc.UltimateListCtrl): + + @eg.LogIt + def __init__(self, parent): + self.lock = threading.Lock() + + ulc.UltimateListCtrl.__init__( + self, + parent, + -1, + size=(600, 200), + agwStyle=( + wx.LC_REPORT | + wx.BORDER_SUNKEN | + wx.LC_EDIT_LABELS | + wx.LC_VRULES | + wx.LC_HRULES | + ulc.ULC_HAS_VARIABLE_ROW_HEIGHT | + ulc.ULC_BORDER_SELECT + ) + ) + self.InsertColumn(0, Text.name_lbl) + self.InsertColumn(1, Text.com_port_lbl) + self.InsertColumn(2, Text.hdmi_lbl) + self.InsertColumn(3, Text.avr_lbl) + self.InsertColumn(4, Text.poll_lbl) + + self.SetColumnWidth(0, 100) + self.SetColumnWidth(1, 120) + self.SetColumnWidth(2, 115) + self.SetColumnWidth(3, 130) + self.SetColumnWidth(4, 130) + + def get_value(): + res = () + + for row in range(self.GetItemCount()): + name_item = self.GetItem(row, 0) + com_item = self.GetItem(row, 1) + hdmi_item = self.GetItem(row, 2) + avr_item = self.GetItem(row, 3) + poll_item = self.GetItem(row, 4) + + adapter_name = str(name_item.GetText()) + com_port = str(com_item.GetText()) + hdmi_port = hdmi_item.GetWindow().GetValue() + use_avr = avr_item.GetWindow().GetValue() + poll_interval = poll_item.GetWindow().GetValue() + + if adapter_name and adapter_name != 'ENTER NAME': + res += (( + com_port, + adapter_name, + hdmi_port, + use_avr, + poll_interval + ),) + return res + + self.GetValue = get_value + + def add_cec_item( + self, + com_port, + adapter_name, + hdmi_port, + use_avr, + poll_interval, + _ + # scan_type + ): + self.lock.acquire() + self.Freeze() + + index = self.InsertStringItem(self.GetItemCount(), adapter_name) + self.SetStringItem(index, 1, com_port) + self.SetStringItem(index, 2, '') + self.SetStringItem(index, 3, '') + self.SetStringItem(index, 4, '') + + com_item = self.GetItem(index, 0) + name_item = self.GetItem(index, 1) + hdmi_item = self.GetItem(index, 2) + avr_item = self.GetItem(index, 3) + poll_item = self.GetItem(index, 4) + + hdmi_port_ctrl = eg.SpinIntCtrl(self, -1, hdmi_port, min=1, max=99) + hdmi_item.SetWindow(hdmi_port_ctrl) + + avr_ctrl = wx.CheckBox(self, -1, '') + avr_ctrl.SetValue(use_avr) + avr_item.SetWindow(avr_ctrl) + + poll_ctrl = eg.SpinNumCtrl( + self, + -1, + poll_interval, + min=0.1, + max=5.0, + increment=0.1 + ) + poll_item.SetWindow(poll_ctrl) + + # if scan_type is None: + # com_item.SetBackgroundColour((255, 255, 75)) + # name_item.SetBackgroundColour((255, 255, 75)) + # hdmi_port_ctrl.SetBackgroundColour((255, 255, 75)) + # hdmi_item.SetBackgroundColour((255, 255, 75)) + # avr_item.SetBackgroundColour((255, 255, 75)) + # + # elif scan_type is False: + # com_item.SetBackgroundColour((255, 0, 0)) + # name_item.SetBackgroundColour((255, 0, 0)) + # hdmi_item.SetBackgroundColour((255, 0, 0)) + # avr_item.SetBackgroundColour((255, 0, 0)) + + self.SetItem(com_item) + self.SetItem(name_item) + self.SetItem(hdmi_item) + self.SetItem(avr_item) + self.SetItem(poll_item) + + self.Thaw() + self.Update() + self.lock.release() + + +class AdapterCtrl(wx.Panel): + + def __init__(self, parent, com_port, adapter_name, adapters): + wx.Panel.__init__(self, parent, -1) + + choices = list( + adapter.name + ' : ' + adapter.com_port + for adapter in adapters + ) + + adapters_st = wx.StaticText(self, -1, Text.adapter_lbl) + adapters_ctrl = eg.Choice(self, 0, choices=sorted(choices)) + + adapters_ctrl.SetStringSelection( + str(adapter_name) + ' : ' + str(com_port) + ) + + def get_value(): + value = adapters_ctrl.GetStringSelection() + a_name, c_port = value.split(' : ') + return c_port, a_name + + adapters_sizer = wx.BoxSizer(wx.HORIZONTAL) + adapters_sizer.Add(adapters_st, 0, wx.EXPAND | wx.ALL, 5) + adapters_sizer.Add(adapters_ctrl, 0, wx.EXPAND | wx.ALL, 5) + + self.GetValue = get_value + self.SetSizer(adapters_sizer) + + +class DeviceCtrl(wx.Panel): + + def __init__(self, parent, dev): + wx.Panel.__init__(self, parent, -1) + + device_st = wx.StaticText(self, -1, Text.device_lbl) + device_ctrl = eg.Choice(self, 0, choices=[]) + sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer.Add(device_st, 0, wx.EXPAND | wx.ALL, 5) + sizer.Add(device_ctrl, 0, wx.EXPAND | wx.ALL, 5) + + def on_choice(evt): + global dev + dev = device_ctrl.GetStringSelection() + evt.Skip() + + device_ctrl.Bind(wx.EVT_CHOICE, on_choice) + + def update_devices(adapter): + choices = list(d.name for d in adapter.devices) + device_ctrl.SetItems(choices) + + if dev in choices: + device_ctrl.SetStringSelection(dev) + else: + device_ctrl.SetSelection(0) + + def get_value(): + return dev + + self.GetValue = get_value + self.UpdateDevices = update_devices + self.SetSizer(sizer) diff --git a/src/EventGhost/egplugin_sources/info.py b/src/EventGhost/egplugin_sources/info.py new file mode 100644 index 00000000..23c8fac0 --- /dev/null +++ b/src/EventGhost/egplugin_sources/info.py @@ -0,0 +1,7 @@ +name = u'Pulse-Eight CEC adapter' +author = u'Lars Op den Kamp, K' +version = u'1.1b' +url = u'http://libcec.pulse-eight.com/' +guid = '{81AC5776-0220-4D2A-B561-DD91F052FF7B}' +description = u'

Integration with libCEC, which adds support for Pulse-Eight\'s CEC adapters.

\n
\n

\n
\ncec.png\n

Notice: Make sure you select the correct HDMI port number on the device that the CEC adapter is connected to, or remote control input won\'t work.

\n' +icon = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAHVYdUCVeJ6QpYiuULWYuRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEUoQBBlSG+U6d0P9PntH/CFaI8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEUoQFBFKECARShAcEUoQFBFKEBARShP5jtOf/ZLTm/wVThf0EUoQFBFKEBwRShAQAAAAAAAAAAAAAAAAAAAAABVOF9gVThfsFU4X8BVOF/AVThf0MWoz3XK7i/2S16P8PXY/3BVOF/QVThfwFU4X2BFKEAwAAAAAAAAAAAAAAAAVThfw9lc3/Q5rR/0mf1f9Po9n/Vajd/1ms4f9fseX/ZLXo/2i36v9ot+r/BFKE/wAAAAAAAAAAAAAAAAAAAAAEUoT9OJHK/z6Wzv9Em9L/SqDW/1Ck2v9VqN3/Wq3i/1+x5f9ltun/Z7bp/wRShP4AAAAAAAAAAAAAAAAAAAAABFKE/QlZi/IIVIr1KX2z+0Wc0/9Lodf/UKTa/1ap3v9cruL/YLLm/2W15/8GVIb8B1WH8wVThfsIVojvCFaIWQRShCQEUoQnBFKETQdVh/hAmND/Rp3U/0uh1/9Rpdv/V6rf/12s4/9is+b/YrLk/2Cw4v9ltef/RJPG/wdVh+0EUoQHBFKEBARShCYGVIb4O5TN/0GZ0f9GndT/TKLY/1Gl2/9Xqt//Xa/j/yV1p/9ot+r/aLfq/2Cw4v8HVYf0BFCG9wRShPwFU4X6HW+p+TeQyf89lc3/Q5rR/0ee1f9Ootj/Uqbc/1ir4P8EUoT/IXGj9E+e0f82hbj+CliKoARQhvwgfrr/JYO//yyIwv8xjcf/OJHK/z2Vzf9DmtH/SZ/V/0+g2f9Vpd3/BFKE/gRShG4JWYvfCFaI4gRShBQEUIb8HHu4/yF/u/8nhL//LInE/zKOyP84kcr/PpbO/0Oa0f9KoNb/UKTa/wRShP4EUoQBAAAAAAAAAAAAAAAABFKE9wVThfkFU4X6BFSG/ARShP0IWIr2L4jB/zeQyf8GVIb8BFKE/gVQhf0FU4X1BFKEAQAAAAAAAAAAAAAAAARShAgEUoQNBFKECgRShAYEUoQDB1OJ0yR8tP8yjcb/BVOF+gAAAAAEUoQEBFKEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARShP0ngrv/LonC/wRShP0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFUYeSBVOF+QRUhvwGVIaaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==' \ No newline at end of file diff --git a/windows/create-installer.cmd b/windows/create-installer.cmd index bc49309e..d82b877e 100644 --- a/windows/create-installer.cmd +++ b/windows/create-installer.cmd @@ -85,8 +85,10 @@ CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\amd64\cecc-client.exe CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\amd64\cec-tray.exe CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\amd64\CecSharpTester.exe + :CREATEINSTALLER echo. Creating the installer +GOTO CREATEEGPLUGIN cd %MYDIR%..\build\x86 copy cec.dll libcec.dll cd ..\amd64 @@ -95,7 +97,10 @@ cd %MYDIR%..\project %NSIS% /V1 /X"SetCompressor /FINAL lzma" "libCEC.nsi" FOR /F "delims=" %%F IN ('dir /b /s "%MYDIR%..\build\libCEC-*.exe" 2^>nul') DO SET INSTALLER=%%F -IF [%INSTALLER%] == [] GOTO :ERRORCREATINGINSTALLER +IF [%INSTALLER%] == [] ( + GOTO EGPLUGINCLEANUP + GOTO ERRORCREATINGINSTALLER +) rem Sign the installer if sign-binary.cmd exists IF EXIST "..\support\private\sign-binary.cmd" ( @@ -105,6 +110,28 @@ IF EXIST "..\support\private\sign-binary.cmd" ( echo. The installer can be found here: %INSTALLER% set EXITCODE=0 +GOTO EGPLUGINCLEANUP +GOTO EXIT + +:CREATEEGPLUGIN +echo. Creating EventGhost plugin file +SET EGSOURCES=%MYDIR%..\src\eventghost\egplugin_sources\ +copy %MYDIR%..\build\x86\python\cec\__init__.py %EGSOURCES%PulseEight\cec +copy %MYDIR%..\build\x86\python\_cec.pyd %EGSOURCES%PulseEight +copy %MYDIR%..\build\x86\cec.dll %EGSOURCES%PulseEight +PowerShell -ExecutionPolicy ByPass -Command "Add-Type -Assembly System.IO.Compression.FileSystem;[System.IO.Compression.ZipFile]::CreateFromDirectory('%EGSOURCES%', '%EGSOURCES%..\pulse_eight.egplugin', [System.IO.Compression.CompressionLevel]::Optimal, $false)" +del %EGSOURCES%PulseEight\cec\__init__.py +del %EGSOURCES%PulseEight\_cec.pyd +del %EGSOURCES%PulseEight\cec.dll +IF NOT EXIST "%EGSOURCES%..\pulse_eight.egplugin"( + GOTO NOEGPLUGIN +) + +:EGPLUGINCLEANUP +del %EGSOURCES%..\pulse_eight.egplugin + +:NOEGPLUGIN +echo. Failed to create the EventGhost plugin file. GOTO EXIT :NOSDK11 From 5bbff702f490862c4f853fdd274b8462b4dd1073 Mon Sep 17 00:00:00 2001 From: maart84 <1885039+maart84@users.noreply.github.com> Date: Tue, 3 Oct 2017 10:53:40 +0200 Subject: [PATCH 26/93] Fix LG TV always changing input when turned on #307 --- src/libcec/implementations/SLCommandHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/implementations/SLCommandHandler.cpp b/src/libcec/implementations/SLCommandHandler.cpp index 51487047..34dec5ab 100644 --- a/src/libcec/implementations/SLCommandHandler.cpp +++ b/src/libcec/implementations/SLCommandHandler.cpp @@ -129,7 +129,7 @@ int CSLCommandHandler::HandleVendorCommand(const cec_command &command) else if (command.parameters.size == 1 && command.parameters[0] == SL_COMMAND_REQUEST_RECONNECT) { - HandleVendorCommandPowerOn(command, false); + HandleVendorCommandPowerOnStatus(command); return COMMAND_HANDLED; } else if (command.parameters.size == 1 && From 126a9f6ed87ac6dcab2b41fd3c711a00fa1964d5 Mon Sep 17 00:00:00 2001 From: "Garrett L. Ward" Date: Tue, 9 Jan 2018 21:12:08 -0500 Subject: [PATCH 27/93] Include C version of libCEC loader when installing --- src/libcec/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcec/CMakeLists.txt b/src/libcec/CMakeLists.txt index 5c888070..6baee69e 100644 --- a/src/libcec/CMakeLists.txt +++ b/src/libcec/CMakeLists.txt @@ -75,6 +75,7 @@ set(CEC_SOURCES_PLATFORM platform/adl/adl-edid.cpp set(CEC_EXT_HEADERS ${PROJECT_SOURCE_DIR}/../../include/cec.h ${PROJECT_SOURCE_DIR}/../../include/cecc.h ${PROJECT_SOURCE_DIR}/../../include/cecloader.h + ${PROJECT_SOURCE_DIR}/../../include/ceccloader.h ${PROJECT_SOURCE_DIR}/../../include/cectypes.h ${PROJECT_SOURCE_DIR}/../../include/version.h) source_group("Header Files (external)" FILES ${CEC_EXT_HEADERS}) @@ -172,6 +173,7 @@ endif() install(FILES ${PROJECT_SOURCE_DIR}/../../include/cec.h ${PROJECT_SOURCE_DIR}/../../include/cecc.h ${PROJECT_SOURCE_DIR}/../../include/cecloader.h + ${PROJECT_SOURCE_DIR}/../../include/ceccloader.h ${PROJECT_SOURCE_DIR}/../../include/cectypes.h ${PROJECT_SOURCE_DIR}/../../include/version.h DESTINATION include/libcec) From 3e6f3acadd677d497f32d98b89c5a372b46b10cf Mon Sep 17 00:00:00 2001 From: Ian Whyman Date: Sat, 13 Jan 2018 10:18:45 +0000 Subject: [PATCH 28/93] Fix build if tinfo library is not present. NCurses supports both having tinfo included or separate, so we need to check for both conditions. Gentoo-Bug: https://bugs.gentoo.org/615634 --- src/cec-client/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cec-client/CMakeLists.txt b/src/cec-client/CMakeLists.txt index 37c733b3..8002d49a 100644 --- a/src/cec-client/CMakeLists.txt +++ b/src/cec-client/CMakeLists.txt @@ -27,8 +27,12 @@ set(cecclient_SOURCES cec-client.cpp) check_library_exists(curses initscr "" HAVE_CURSES_API) if (HAVE_CURSES_API) list(APPEND cecclient_SOURCES curses/CursesControl.cpp) + + # tinfo + find_library(HAVE_CURSES_TINFO tinfo) endif() + add_executable(cec-client ${cecclient_SOURCES}) set_target_properties(cec-client PROPERTIES VERSION ${LIBCEC_VERSION_MAJOR}.${LIBCEC_VERSION_MINOR}.${LIBCEC_VERSION_PATCH}) target_link_libraries(cec-client ${p8-platform_LIBRARIES}) @@ -44,7 +48,9 @@ if (NOT WIN32) # curses if (HAVE_CURSES_API) target_link_libraries(cec-client curses) - target_link_libraries(cec-client tinfo) + if (HAVE_CURSES_TINFO) + target_link_libraries(cec-client tinfo) + endif() endif() # rt From feacdd32ce458f31752606e904b8cc4e3c8487a3 Mon Sep 17 00:00:00 2001 From: Brett Keller Date: Wed, 21 Mar 2018 18:21:02 -0500 Subject: [PATCH 29/93] Fix broken Python version check and failure to build on cmake < 3.7 Ubuntu 16.04 and derivative distributions ship cmake 3.5, which does not support GREATER_EQUAL. Tested fix on Ubuntu MATE 16.04 for Raspberry Pi. Also, the existing logic to check for Python 2.7+ does not work. This patch corrects that, and I was able to 'import cec' in Python 3 on Ubuntu MATE 16.04 for Raspberry Pi without error. --- src/libcec/cmake/CheckPlatformSupport.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index 73612dec..d9e1e41b 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -179,6 +179,8 @@ else() else() string(REGEX REPLACE "\\.[0-9]+\\+?$" "" PYTHON_VERSION ${PYTHONLIBS_VERSION_STRING}) endif() + string(REGEX REPLACE "\\..*$" "" PYTHON_MAJOR_VERSION ${PYTHON_VERSION}) + string(REGEX REPLACE "^.*\\." "" PYTHON_MINOR_VERSION ${PYTHON_VERSION}) include(${SWIG_USE_FILE}) include_directories(${PYTHON_INCLUDE_PATH}) @@ -190,10 +192,10 @@ else() swig_link_libraries(cec cec) SET(PYTHON_LIB_INSTALL_PATH "/cec" CACHE STRING "python lib path") - if (${CMAKE_MAJOR_VERSION} GREATER 2 AND ${CMAKE_MAJOR_VERSION} GREATER_EQUAL 7) + if (${PYTHON_MAJOR_VERSION} EQUAL 2 AND ${PYTHON_MINOR_VERSION} GREATER 6) SET(PYTHON_LIB_INSTALL_PATH "" CACHE STRING "python lib path" FORCE) else() - if (${CMAKE_MAJOR_VERSION} GREATER_EQUAL 3) + if (${PYTHON_MAJOR_VERSION} GREATER 2) SET(PYTHON_LIB_INSTALL_PATH "" CACHE STRING "python lib path" FORCE) endif() endif() From 16e8c072709572ccefa6c7f835136db01f031323 Mon Sep 17 00:00:00 2001 From: Pascal Bach Date: Fri, 28 Sep 2018 16:56:14 +0200 Subject: [PATCH 30/93] Explicitly use python3 in pyCecClient --- src/pyCecClient/pyCecClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyCecClient/pyCecClient.py b/src/pyCecClient/pyCecClient.py index 6d478577..d0504ac6 100755 --- a/src/pyCecClient/pyCecClient.py +++ b/src/pyCecClient/pyCecClient.py @@ -1,4 +1,4 @@ -#! /usr/bin/python +#! /usr/bin/python3 ## demo of the python-libcec API # This file is part of the libCEC(R) library. From d8edd75af18c2fcfa0d8502436fae370071f1753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jernej=20Fija=C4=8Dko?= Date: Tue, 2 Oct 2018 23:24:34 +0200 Subject: [PATCH 31/93] Fix Pulse-Eight USB CEC adapter detection on macOS Mojave --- src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp index f2fe2f59..d3efc193 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp @@ -270,7 +270,7 @@ uint8_t CUSBCECAdapterDetection::FindAdaptersApple(cec_adapter_descriptor *devic CFMutableDictionaryRef classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); if (classesToMatch) { - CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDModemType)); + CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); kresult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &serialPortIterator); if (kresult == KERN_SUCCESS) { From 2c41938e000026725487cc0f0ba4b82f11a70837 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Fri, 2 Nov 2018 12:09:41 +0100 Subject: [PATCH 32/93] changed: disable autonomous mode when a Samsung TV is connected Samsung 2017+ TVs will power on randomly (Samsung bug) closes #424 --- src/libcec/CECProcessor.cpp | 7 +++++++ src/libcec/CECProcessor.h | 1 + .../adapter/AOCEC/AOCECAdapterCommunication.h | 1 + src/libcec/adapter/AdapterCommunication.h | 8 +++++++- .../adapter/Exynos/ExynosCECAdapterCommunication.h | 1 + .../adapter/Pulse-Eight/USBCECAdapterCommands.cpp | 1 - .../adapter/Pulse-Eight/USBCECAdapterCommands.h | 14 +++++++------- .../Pulse-Eight/USBCECAdapterCommunication.cpp | 7 +++++++ .../Pulse-Eight/USBCECAdapterCommunication.h | 1 + .../adapter/RPi/RPiCECAdapterCommunication.h | 1 + .../TDA995x/TDA995xCECAdapterCommunication.h | 5 +++-- src/libcec/implementations/ANCommandHandler.cpp | 4 ++++ 12 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/libcec/CECProcessor.cpp b/src/libcec/CECProcessor.cpp index 44caa164..c67415cf 100644 --- a/src/libcec/CECProcessor.cpp +++ b/src/libcec/CECProcessor.cpp @@ -683,6 +683,13 @@ bool CCECProcessor::PersistConfiguration(const libcec_configuration &configurati return m_communication ? m_communication->PersistConfiguration(persistConfiguration) : false; } +bool CCECProcessor::SetAutoMode(bool automode) +{ + return !!m_communication ? + m_communication->SetAutoMode(automode) : + false; +} + void CCECProcessor::RescanActiveDevices(void) { for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++) diff --git a/src/libcec/CECProcessor.h b/src/libcec/CECProcessor.h index 20d792a9..d93333f1 100644 --- a/src/libcec/CECProcessor.h +++ b/src/libcec/CECProcessor.h @@ -132,6 +132,7 @@ namespace CEC uint8_t GetRetryLineTimeout(void); bool CanPersistConfiguration(void); bool PersistConfiguration(const libcec_configuration &configuration); + bool SetAutoMode(bool automode); void RescanActiveDevices(void); bool SetLineTimeout(uint8_t iTimeout); diff --git a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h index b7fa9a66..dc494264 100644 --- a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h +++ b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h @@ -72,6 +72,7 @@ namespace CEC uint32_t GetFirmwareBuildDate(void) { return 0; } bool IsRunningLatestFirmware(void) { return true; } bool PersistConfiguration(const libcec_configuration & UNUSED(configuration)) { return false; } + bool SetAutoMode(bool UNUSED(automode)) { return false; } bool GetConfiguration(libcec_configuration & UNUSED(configuration)) { return false; } std::string GetPortName(void) { return std::string("AOCEC"); } uint16_t GetPhysicalAddress(void); diff --git a/src/libcec/adapter/AdapterCommunication.h b/src/libcec/adapter/AdapterCommunication.h index 74c80751..cac2bc97 100644 --- a/src/libcec/adapter/AdapterCommunication.h +++ b/src/libcec/adapter/AdapterCommunication.h @@ -176,11 +176,17 @@ namespace CEC /*! * @brief Persist the given configuration in adapter (if supported) - * @brief The configuration to store. + * @param configuration The configuration to store. * @return True when the configuration was persisted, false otherwise. */ virtual bool PersistConfiguration(const libcec_configuration &configuration) = 0; + /*! + * @brief Enable or disable auto mode (only supported by P8 USB-CEC) + * @param automode true to enable, false to disable + */ + virtual bool SetAutoMode(bool automode) = 0; + /*! * @brief Get the persisted configuration from the adapter (if supported) * @param configuration The updated configuration. diff --git a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h index 728fddfe..6668aa65 100644 --- a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h +++ b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h @@ -71,6 +71,7 @@ namespace CEC bool IsRunningLatestFirmware(void) { return true; } bool PersistConfiguration(const libcec_configuration & UNUSED(configuration)) { return false; } bool GetConfiguration(libcec_configuration & UNUSED(configuration)) { return false; } + bool SetAutoMode(bool UNUSED(automode)) { return false; } std::string GetPortName(void) { return std::string("EXYNOS"); } uint16_t GetPhysicalAddress(void); bool SetControlledMode(bool UNUSED(controlled)) { return true; } diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp index 19eaa169..c1474f1f 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp @@ -498,7 +498,6 @@ bool CUSBCECAdapterCommands::PersistConfiguration(const libcec_configuration &co if (!RequestSettings()) return bReturn; - bReturn |= SetSettingAutoEnabled(true); bReturn |= SetSettingDeviceType(CLibCEC::GetType(configuration.logicalAddresses.primary)); bReturn |= SetSettingDefaultLogicalAddress(configuration.logicalAddresses.primary); bReturn |= SetSettingLogicalAddressMask(CLibCEC::GetMaskForType(configuration.logicalAddresses.primary)); diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h index 181f9443..24f16537 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h @@ -135,6 +135,13 @@ namespace CEC void SetActiveSource(bool bSetTo, bool bClientUnregistered); + /*! + * @brief Change the value of the "auto enabled" setting. + * @param enabled The new value. + * @return True when changed and set, false otherwise. + */ + bool SetSettingAutoEnabled(bool enabled); + private: /*! * @brief Reads all settings from the eeprom. @@ -149,13 +156,6 @@ namespace CEC */ cec_datapacket RequestSetting(cec_adapter_messagecode msgCode); - /*! - * @brief Change the value of the "auto enabled" setting. - * @param enabled The new value. - * @return True when changed and set, false otherwise. - */ - bool SetSettingAutoEnabled(bool enabled); - /*! * @brief Request the value of the "auto enabled" setting from the adapter. * @return True when retrieved, false otherwise. diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp index c30fa7a5..323ac13b 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp @@ -648,6 +648,13 @@ bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuratio false; } +bool CUSBCECAdapterCommunication::SetAutoMode(bool automode) +{ + return IsOpen() ? + m_commands->SetSettingAutoEnabled(automode) && m_eepromWriteThread->Write() : + false; +} + bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration) { return IsOpen() ? m_commands->GetConfiguration(configuration) : false; diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h index 589ef7b5..c7978ca9 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h @@ -83,6 +83,7 @@ namespace CEC uint32_t GetFirmwareBuildDate(void); bool IsRunningLatestFirmware(void); bool PersistConfiguration(const libcec_configuration &configuration); + bool SetAutoMode(bool automode); bool GetConfiguration(libcec_configuration &configuration); std::string GetPortName(void); uint16_t GetPhysicalAddress(void); diff --git a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h index 9406ebd4..5e3318c3 100644 --- a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h +++ b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h @@ -78,6 +78,7 @@ namespace CEC bool IsRunningLatestFirmware(void) { return true; }; bool PersistConfiguration(const libcec_configuration & UNUSED(configuration)) { return false; }; bool GetConfiguration(libcec_configuration & UNUSED(configuration)) { return false; }; + bool SetAutoMode(bool UNUSED(automode)) { return false; } std::string GetPortName(void) { std::string strReturn("RPI"); return strReturn; }; uint16_t GetPhysicalAddress(void); bool SetControlledMode(bool UNUSED(controlled)) { return true; }; diff --git a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h index d6f7aa34..40e46013 100644 --- a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h +++ b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h @@ -82,6 +82,7 @@ namespace CEC bool IsRunningLatestFirmware(void) { return true; } bool PersistConfiguration(const libcec_configuration & UNUSED(configuration)) { return false; } bool GetConfiguration(libcec_configuration & UNUSED(configuration)) { return false; } + bool SetAutoMode(bool UNUSED(automode)) { return false; } std::string GetPortName(void) { return std::string("TDA995X"); } uint16_t GetPhysicalAddress(void); bool SetControlledMode(bool UNUSED(controlled)) { return true; } @@ -109,12 +110,12 @@ namespace CEC mutable P8PLATFORM::CMutex m_mutex; P8PLATFORM::CCDevSocket *m_dev; /**< the device connection */ - + P8PLATFORM::CMutex m_messageMutex; uint32_t m_iNextMessage; std::map m_messages; }; - + }; #endif diff --git a/src/libcec/implementations/ANCommandHandler.cpp b/src/libcec/implementations/ANCommandHandler.cpp index e6f3fd26..c8e19619 100644 --- a/src/libcec/implementations/ANCommandHandler.cpp +++ b/src/libcec/implementations/ANCommandHandler.cpp @@ -53,6 +53,10 @@ CANCommandHandler::CANCommandHandler(CCECBusDevice *busDevice, { m_vendorId = CEC_VENDOR_SAMSUNG; m_bOPTSendDeckStatusUpdateOnActiveSource = false; + if (busDevice->GetLogicalAddress() == CECDEVICE_TV) { + // disable auto mode, as this may way up the TV randomly (samsung 2017+ bug) + m_busDevice->GetProcessor()->SetAutoMode(false); + } } int CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command) From e418800569f1ece1a8d0a750c30dd6f9b86c9183 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Fri, 2 Nov 2018 12:34:04 +0100 Subject: [PATCH 33/93] Revert "added: -std=c++11 to pkg-config" This reverts commit 456c7a62ad82f43f630a3e1224afb47f6d99082c. See https://github.com/Pulse-Eight/libcec/issues/266#issuecomment-371883129 --- src/libcec/libcec.pc.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/libcec.pc.in b/src/libcec/libcec.pc.in index 00de792b..2b9f77ac 100644 --- a/src/libcec/libcec.pc.in +++ b/src/libcec/libcec.pc.in @@ -9,4 +9,4 @@ URL: http://www.pulse-eight.com/ Version: @LIBCEC_VERSION_MAJOR@.@LIBCEC_VERSION_MINOR@.@LIBCEC_VERSION_PATCH@ Requires: @LIBCEC_LIBREQUIRES@ Libs: -L${libdir} -lcec -Cflags: -I${includedir} -I${includedir}/libcec -std=c++11 +Cflags: -I${includedir} -I${includedir}/libcec From a3bfedf4bc3f0586eef27889f6e775a054191a05 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Fri, 2 Nov 2018 12:35:41 +0100 Subject: [PATCH 34/93] cosmetics. thanks @MilhouseVH --- src/libcec/implementations/ANCommandHandler.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libcec/implementations/ANCommandHandler.cpp b/src/libcec/implementations/ANCommandHandler.cpp index c8e19619..19464ccb 100644 --- a/src/libcec/implementations/ANCommandHandler.cpp +++ b/src/libcec/implementations/ANCommandHandler.cpp @@ -53,8 +53,9 @@ CANCommandHandler::CANCommandHandler(CCECBusDevice *busDevice, { m_vendorId = CEC_VENDOR_SAMSUNG; m_bOPTSendDeckStatusUpdateOnActiveSource = false; - if (busDevice->GetLogicalAddress() == CECDEVICE_TV) { - // disable auto mode, as this may way up the TV randomly (samsung 2017+ bug) + if (busDevice->GetLogicalAddress() == CECDEVICE_TV) + { + // disable auto mode, as this may wake up the TV randomly (samsung 2017+ bug) m_busDevice->GetProcessor()->SetAutoMode(false); } } From d62951c8eb37f9bcaba44fe9d5a9ba7c4fcd3d1b Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Fri, 2 Nov 2018 12:43:30 +0100 Subject: [PATCH 35/93] fixed: CRPiCECAdapterMessageQueue::Write() accessed the queue without locking issue #423 --- src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.cpp b/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.cpp index 4d140d28..1f827204 100644 --- a/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.cpp +++ b/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.cpp @@ -78,7 +78,7 @@ bool CRPiCECAdapterMessageQueueEntry::MessageReceived(cec_opcode opcode, cec_log m_command.initiator == initiator && m_command.destination == destination) || - (!m_command.opcode_set && + (!m_command.opcode_set && m_command.destination == destination)) { CLockObject lock(m_mutex); @@ -218,6 +218,7 @@ cec_adapter_message_state CRPiCECAdapterMessageQueue::Write(const cec_command &c if (iReturn != VCHIQ_SUCCESS) { LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending command '%s' failed (%d)", CCECTypeUtils::ToString(command.opcode), iReturn); + CLockObject lock(m_mutex); delete entry; m_messages.erase(iEntryId); return ADAPTER_MESSAGE_STATE_ERROR; @@ -256,4 +257,3 @@ cec_adapter_message_state CRPiCECAdapterMessageQueue::Write(const cec_command &c } #endif - From 0055a83a705e86ba749a39fa5cc9b1871be739b8 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Fri, 2 Nov 2018 13:03:10 +0100 Subject: [PATCH 36/93] changed: use sig_atomic_t for shared object between sig handler and main issue #425 --- src/cec-client/cec-client.cpp | 43 ++++++++++++++++++----------------- src/cecc-client/cecc-client.c | 16 ++++++------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/cec-client/cec-client.cpp b/src/cec-client/cec-client.cpp index a66c751f..7fdaebc7 100644 --- a/src/cec-client/cec-client.cpp +++ b/src/cec-client/cec-client.cpp @@ -56,20 +56,20 @@ using namespace P8PLATFORM; static void PrintToStdOut(const char *strFormat, ...); -ICECCallbacks g_callbacks; -libcec_configuration g_config; -int g_cecLogLevel(-1); -int g_cecDefaultLogLevel(CEC_LOG_ALL); -std::ofstream g_logOutput; -bool g_bShortLog(false); -std::string g_strPort; -bool g_bSingleCommand(false); -bool g_bExit(false); -bool g_bHardExit(false); -CMutex g_outputMutex; -ICECAdapter* g_parser; +ICECCallbacks g_callbacks; +libcec_configuration g_config; +int g_cecLogLevel(-1); +int g_cecDefaultLogLevel(CEC_LOG_ALL); +std::ofstream g_logOutput; +bool g_bShortLog(false); +std::string g_strPort; +bool g_bSingleCommand(false); +volatile sig_atomic_t g_bExit(0); +bool g_bHardExit(false); +CMutex g_outputMutex; +ICECAdapter* g_parser; #if defined(HAVE_CURSES_API) -bool g_cursesEnable(false); +bool g_cursesEnable(false); CCursesControl g_cursesControl("1", "0"); #endif @@ -92,7 +92,7 @@ class CReconnect : public P8PLATFORM::CThread if (!g_parser->Open(g_strPort.c_str())) { PrintToStdOut("Failed to reconnect\n"); - g_bExit = true; + g_bExit = 1; } } return NULL; @@ -662,7 +662,7 @@ bool ProcessCommandBL(ICECAdapter *parser, const std::string &command, std::stri if (parser->StartBootloader()) { PrintToStdOut("entered bootloader mode. exiting cec-client"); - g_bExit = true; + g_bExit = 1; g_bHardExit = true; } return true; @@ -1213,11 +1213,7 @@ bool ProcessCommandLineArguments(int argc, char *argv[]) void sighandler(int iSignal) { PrintToStdOut("signal caught: %d - exiting", iSignal); - g_bExit = true; -#if defined(HAVE_CURSES_API) - if (g_cursesEnable) - g_cursesControl.End(); -#endif + g_bExit = 1; } int main (int argc, char *argv[]) @@ -1354,7 +1350,7 @@ int main (int argc, char *argv[]) if (g_cursesEnable) g_cursesControl.End(); #endif - g_bExit = true; + g_bExit = 1; } if (!g_bExit && !g_bHardExit) @@ -1367,5 +1363,10 @@ int main (int argc, char *argv[]) if (g_logOutput.is_open()) g_logOutput.close(); +#if defined(HAVE_CURSES_API) + if (g_cursesEnable) + g_cursesControl.End(); +#endif + return 0; } diff --git a/src/cecc-client/cecc-client.c b/src/cecc-client/cecc-client.c index 33731095..e846b1bb 100644 --- a/src/cecc-client/cecc-client.c +++ b/src/cecc-client/cecc-client.c @@ -68,14 +68,14 @@ static ICECCallbacks g_callbacks = { .sourceActivated = NULL }; -static libcec_configuration g_config; -static int g_cecLogLevel = -1; -static int g_cecDefaultLogLevel = CEC_LOG_ALL; -static char g_strPort[50] = { 0 }; -static int g_bSingleCommand = 0; -static int g_bExit = 0; -static int g_bHardExit = 0; -static libcec_interface_t g_iface; +static libcec_configuration g_config; +static int g_cecLogLevel = -1; +static int g_cecDefaultLogLevel = CEC_LOG_ALL; +static char g_strPort[50] = { 0 }; +static int g_bSingleCommand = 0; +static volatile sig_atomic_t g_bExit = 0; +static int g_bHardExit = 0; +static libcec_interface_t g_iface; static void sighandler(int iSignal) { From 96ba7dd54c5082323798d53d2315e2d4c5d6b434 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Fri, 2 Nov 2018 17:22:13 +0100 Subject: [PATCH 37/93] added links to examples for rpi --- docs/README.raspberrypi.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/README.raspberrypi.md b/docs/README.raspberrypi.md index feb6cc17..d8806b70 100644 --- a/docs/README.raspberrypi.md +++ b/docs/README.raspberrypi.md @@ -37,4 +37,9 @@ cmake -DRPI_INCLUDE_DIR=/opt/vc/include -DRPI_LIB_DIR=/opt/vc/lib .. make -j4 sudo make install sudo ldconfig -``` \ No newline at end of file +``` + +## Examples +Example implementations using libCEC can be found here: +* [github.com/Pulse-Eight/libcec/blob/master/src/cec-client/cec-client.cpp](https://github.com/Pulse-Eight/libcec/blob/master/src/cec-client/cec-client.cpp) +* [github.com/DrGeoff/cec_simplest](https://github.com/DrGeoff/cec_simplest) From aec4cb5d3b75ccede0476af88415c8b1e53db9ad Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Mon, 5 Nov 2018 18:03:28 +0100 Subject: [PATCH 38/93] fixed: python 3.5+ import issue #356 --- debian/python-libcec.install | 2 +- src/libcec/cmake/CheckPlatformSupport.cmake | 34 +++++++++++++++------ src/libcec/cmake/__init__.py | 1 + 3 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 src/libcec/cmake/__init__.py diff --git a/debian/python-libcec.install b/debian/python-libcec.install index 58f77101..82e69625 100644 --- a/debian/python-libcec.install +++ b/debian/python-libcec.install @@ -1,2 +1,2 @@ -usr/lib/python*/dist-packages/cec/* +usr/lib/python*/dist-packages/* usr/bin/pyCecClient diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index d9e1e41b..56ab01ef 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -201,11 +201,17 @@ else() endif() if(WIN32) - install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} - DESTINATION python/${PYTHON_LIB_INSTALL_PATH}) - install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cec.py - DESTINATION python/cec - RENAME __init__.py) + if (${PYTHON_MAJOR_VERSION} EQUAL 2) + install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} + DESTINATION python/${PYTHON_LIB_INSTALL_PATH}/cec) + install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cmake/__init__.py + DESTINATION python/cec) + else() + install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} + DESTINATION python/${PYTHON_LIB_INSTALL_PATH}) + install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cec.py + DESTINATION python) + endif() else() if(EXISTS "/etc/os-release") file(READ "/etc/os-release" OS_RELEASE) @@ -219,11 +225,19 @@ else() SET(PYTHON_PKG_DIR "site-packages") endif() - install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} - DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}/${PYTHON_LIB_INSTALL_PATH}) - install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cec.py - DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}/cec - RENAME __init__.py) + if (${PYTHON_MAJOR_VERSION} EQUAL 2) + install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} + DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}/${PYTHON_LIB_INSTALL_PATH}/cec) + install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cec.py + DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}) + install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cmake/__init__.py + DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}/cec) + else() + install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} + DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}/${PYTHON_LIB_INSTALL_PATH}) + install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cec.py + DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}) + endif() endif() endif() endif() diff --git a/src/libcec/cmake/__init__.py b/src/libcec/cmake/__init__.py new file mode 100644 index 00000000..9658129c --- /dev/null +++ b/src/libcec/cmake/__init__.py @@ -0,0 +1 @@ +## dummy import for Python 2.x ## From db58911b8002d6f2694babd9b322fcfd958cdf72 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Tue, 6 Nov 2018 16:17:23 +0100 Subject: [PATCH 39/93] fixed: take dummy __init__.py from the source dir issue #356 --- src/libcec/cmake/CheckPlatformSupport.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index 56ab01ef..b7773ee1 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -204,7 +204,7 @@ else() if (${PYTHON_MAJOR_VERSION} EQUAL 2) install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} DESTINATION python/${PYTHON_LIB_INSTALL_PATH}/cec) - install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cmake/__init__.py + install(FILES ${CMAKE_SOURCE_DIR}/src/libcec/cmake/__init__.py DESTINATION python/cec) else() install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} @@ -230,7 +230,7 @@ else() DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}/${PYTHON_LIB_INSTALL_PATH}/cec) install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cec.py DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}) - install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cmake/__init__.py + install(FILES ${CMAKE_SOURCE_DIR}/src/libcec/cmake/__init__.py DESTINATION lib/python${PYTHON_VERSION}/${PYTHON_PKG_DIR}/cec) else() install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} From edb8b7765efee0efcfeefa310dfe2b180c62d0cd Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Tue, 6 Nov 2018 16:18:24 +0100 Subject: [PATCH 40/93] fixed: 13 char device name got truncated --- .../adapter/Pulse-Eight/USBCECAdapterCommands.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp index c1474f1f..92b33b7a 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp @@ -231,21 +231,18 @@ bool CUSBCECAdapterCommands::RequestSettingOSDName(void) LIB_CEC->AddLog(CEC_LOG_DEBUG, "requesting OSD name setting"); #endif - memset(m_persistedConfiguration.strDeviceName, 0, 13); cec_datapacket response = RequestSetting(MSGCODE_GET_OSD_NAME); if (response.size == 0) { LIB_CEC->AddLog(CEC_LOG_DEBUG, "no persisted device name setting"); + memset(m_persistedConfiguration.strDeviceName, 0, 13); return false; } - char buf[14]; - for (uint8_t iPtr = 0; iPtr < response.size && iPtr < 13; iPtr++) - buf[iPtr] = (char)response[iPtr]; - buf[response.size] = 0; - - snprintf(m_persistedConfiguration.strDeviceName, 13, "%s", buf); - LIB_CEC->AddLog(CEC_LOG_DEBUG, "using persisted device name setting: '%s'", buf); + memcpy(m_persistedConfiguration.strDeviceName, response.data, response.size <= 13 ? response.size : 13); + if (response.size < 13) { + m_persistedConfiguration.strDeviceName[response.size] = (char)0; + } return true; } From f87328dd966bc3cf17c0aeb901d7abf5ec9f390e Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 7 Nov 2018 11:28:37 +0100 Subject: [PATCH 41/93] fixed: don't poll samsung TVs. issue #424 --- src/libcec/devices/CECBusDevice.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcec/devices/CECBusDevice.cpp b/src/libcec/devices/CECBusDevice.cpp index 05925a07..640487ef 100644 --- a/src/libcec/devices/CECBusDevice.cpp +++ b/src/libcec/devices/CECBusDevice.cpp @@ -844,6 +844,8 @@ cec_bus_device_status CCECBusDevice::GetStatus(bool bForcePoll /* = false */, bo status = m_deviceStatus; bNeedsPoll = !bSuppressPoll && m_deviceStatus != CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC && + // don't poll Samsung TVs because they can power on randomly + (m_processor->GetDevice(CECDEVICE_TV)->GetCurrentVendorId() != CEC_VENDOR_SAMSUNG) && // poll forced (bForcePoll || // don't know the status From 009d54b3a263bec44de239fcb35eb9a8394239cc Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 7 Nov 2018 16:43:18 +0100 Subject: [PATCH 42/93] fixed: creating windows installer --- project/libCEC.nsi | 2 +- src/libcec/cmake/CheckPlatformSupport.cmake | 11 ++++------- windows/build.cmd | 5 +++-- windows/create-installer.cmd | 8 ++++---- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/project/libCEC.nsi b/project/libCEC.nsi index 7933edcd..30c495b9 100644 --- a/project/libCEC.nsi +++ b/project/libCEC.nsi @@ -200,8 +200,8 @@ Section "Python bindings" SecPythonCec ; Copy to the installation directory SetOutPath "$INSTDIR\python" File "..\build\x86\python\pyCecClient.py" - File "..\build\x86\python\_cec.pyd" SetOutPath "$INSTDIR\python\cec" + File "..\build\x86\python\cec\_cec.pyd" File "..\build\x86\python\cec\__init__.py" SectionEnd diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index b7773ee1..2d7102f3 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -201,16 +201,13 @@ else() endif() if(WIN32) + install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} + DESTINATION python/${PYTHON_LIB_INSTALL_PATH}) + install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cec.py + DESTINATION python/cec) if (${PYTHON_MAJOR_VERSION} EQUAL 2) - install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} - DESTINATION python/${PYTHON_LIB_INSTALL_PATH}/cec) install(FILES ${CMAKE_SOURCE_DIR}/src/libcec/cmake/__init__.py DESTINATION python/cec) - else() - install(TARGETS ${SWIG_MODULE_cec_REAL_NAME} - DESTINATION python/${PYTHON_LIB_INSTALL_PATH}) - install(FILES ${CMAKE_BINARY_DIR}/src/libcec/cec.py - DESTINATION python) endif() else() if(EXISTS "/etc/os-release") diff --git a/windows/build.cmd b/windows/build.cmd index 4f5b634c..417748ec 100644 --- a/windows/build.cmd +++ b/windows/build.cmd @@ -19,16 +19,17 @@ IF NOT EXIST "%MYDIR%..\src\platform\windows\build.cmd" ( GOTO exit ) -rmdir /s /q %MYDIR%..\build +rmdir /s /q %MYDIR%..\build 2> nul FOR %%T IN (amd64 x86) DO ( + echo Run %MYDIR%build-lib.cmd %%T CALL %MYDIR%build-lib.cmd %%T %BUILDTYPE% %VSVERSION% %INSTALLPATH% nmake IF NOT ERRORLEVEL 0 ( GOTO builderror ) ) -rmdir /s /q %MYDIR%..\build\cmake +rmdir /s /q %MYDIR%..\build\cmake 2> nul exit /b 0 :builderror diff --git a/windows/create-installer.cmd b/windows/create-installer.cmd index d82b877e..e38993e7 100644 --- a/windows/create-installer.cmd +++ b/windows/create-installer.cmd @@ -25,8 +25,8 @@ IF "%VS120COMNTOOLS%"=="" ( set COMPILER12="%VS120COMNTOOLS%\..\IDE\devenv.com" ) ELSE GOTO NOSDK11 -rmdir /s /q %MYDIR%..\build -call build.cmd +rmdir /s /q %MYDIR%..\build 2> nul +call %MYDIR%build.cmd IF NOT ERRORLEVEL 0 ( GOTO ERRORCREATINGINSTALLER ) @@ -65,8 +65,8 @@ copy ..\build\x86\cec-tray.exe %MYDIR%..\build\x86\cec-tray.exe cd "%MYDIR%..\project" rem Clean things up before creating the installer -del /q /f %MYDIR%..\build\x86\LibCecSharp.pdb -del /q /f %MYDIR%..\build\amd64\LibCecSharp.pdb +del /q /f %MYDIR%..\build\x86\LibCecSharp.pdb 2> nul +del /q /f %MYDIR%..\build\amd64\LibCecSharp.pdb 2> nul rem Check for sign-binary.cmd, only present on the Pulse-Eight production build system rem Calls signtool.exe and signs the DLLs with Pulse-Eight's code signing key From 6c76f5f1e73407c033c89ac0bc9a2470708c72e1 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Thu, 8 Nov 2018 16:42:33 +0100 Subject: [PATCH 43/93] changed/fixed: install cec-client by default. correct vs redist --- project/libCEC.nsi | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/project/libCEC.nsi b/project/libCEC.nsi index 30c495b9..c6f7bd98 100644 --- a/project/libCEC.nsi +++ b/project/libCEC.nsi @@ -42,9 +42,9 @@ Var EventGhostLocation !insertmacro MUI_LANGUAGE "English" +InstType "Full installation" InstType "USB-CEC Driver & libCEC" InstType "USB-CEC Driver Only" -InstType "Full installation" Section "USB-CEC Driver" SecDriver SetShellVarContext current @@ -110,7 +110,7 @@ SectionEnd Section "libCEC" SecLibCec SetShellVarContext current - SectionIn 1 3 + SectionIn 1 2 ; Copy to the installation directory SetOutPath "$INSTDIR" @@ -137,7 +137,7 @@ SectionEnd Section "CEC Debug Client" SecCecClient SetShellVarContext current - SectionIn 3 + SectionIn 1 ; Copy to the installation directory SetOutPath "$INSTDIR" @@ -166,7 +166,7 @@ SectionEnd Section "libCEC Tray" SecDotNet SetShellVarContext current - SectionIn 1 3 + SectionIn 1 2 ; Copy to the installation directory SetOutPath "$INSTDIR" @@ -195,7 +195,7 @@ SectionEnd Section "Python bindings" SecPythonCec SetShellVarContext current - SectionIn 1 3 + SectionIn 1 2 ; Copy to the installation directory SetOutPath "$INSTDIR\python" @@ -236,12 +236,12 @@ FunctionEnd !define EVENTGHOST_SECTIONNAME "EventGhost plugin" Section "" SecEvGhostCec SetShellVarContext current - SectionIn 1 3 + SectionIn 1 2 ${If} $EventGhostLocation != "" ; We get the directory of the installer then pass it to GetParentDirectory ; which we then append the path to the plugin file to the returned value - : This is done because EventGhost needs to see the full path to the plugin + ; This is done because EventGhost needs to see the full path to the plugin ; file. Push $EXEDIR Call GetParentDirectory @@ -253,7 +253,7 @@ SectionEnd !define REDISTRIBUTABLE_X86_SECTIONNAME "Microsoft Visual C++ 2015 Redistributable Package (x86)" Section "" SecVCRedistX86 SetShellVarContext current - SectionIn 1 3 + SectionIn 1 2 3 SetOutPath "$TEMP\vc2015_x86" @@ -268,7 +268,7 @@ SectionEnd !define REDISTRIBUTABLE_X64_SECTIONNAME "Microsoft Visual C++ 2015 Redistributable Package (x64)" Section "" SecVCRedistX64 SetShellVarContext current - SectionIn 1 3 + SectionIn 1 2 3 SetOutPath "$TEMP\vc2015_x64" @@ -281,8 +281,8 @@ Section "" SecVCRedistX64 SectionEnd Function .onInit - ; check for vc2013 x86 redist - ReadRegDword $1 HKLM "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{37B55901-995A-3650-80B1-BBFD047E2911}" "BundleVersion" + ; check for vc2015 x86 redist + ReadRegDword $1 HKLM "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{462f63a8-6347-4894-a1b3-dbfe3a4c981d}" "BundleVersion" ${If} $1 != "" StrCpy $VSRedistInstalledX86 "Yes" ${Endif} @@ -297,7 +297,7 @@ Function .onInit ${If} ${RunningX64} ; check for vc2015 x64 redist - ReadRegDword $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{FAAD7243-0141-3987-AA2F-E56B20F80E41}" "BundleVersion" + ReadRegDword $1 HKLM "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{323dad84-0974-4d90-a1c1-e006c7fdbb7d}" "BundleVersion" ${If} $1 != "" StrCpy $VSRedistInstalledX64 "Yes" ${Endif} From d8bd94882770581b3ce4c9c43b930ada1839206c Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Thu, 8 Nov 2018 16:43:14 +0100 Subject: [PATCH 44/93] changed: build with vs 2015 --- windows/build.cmd | 4 +-- windows/create-installer.cmd | 60 ++++++++++++++++++------------------ windows/visual-studio.cmd | 2 +- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/windows/build.cmd b/windows/build.cmd index 417748ec..7dc42c96 100644 --- a/windows/build.cmd +++ b/windows/build.cmd @@ -6,7 +6,7 @@ SETLOCAL SET MYDIR=%~dp0 SET BUILDTYPE=Release -SET VSVERSION=12 +SET VSVERSION=14 SET INSTALLPATH=%MYDIR%..\build IF NOT EXIST "%MYDIR%..\support\windows\cmake\build.cmd" ( @@ -36,4 +36,4 @@ exit /b 0 echo "Failed to build" :exit -exit /b 1 \ No newline at end of file +exit /b 1 diff --git a/windows/create-installer.cmd b/windows/create-installer.cmd index e38993e7..d08c08f3 100644 --- a/windows/create-installer.cmd +++ b/windows/create-installer.cmd @@ -16,14 +16,14 @@ IF EXIST "%ProgramFiles%\NSIS\makensis.exe" ( set NSIS="%ProgramFiles(x86)%\NSIS\makensis.exe" ) ELSE GOTO NONSIS -rem Check for VC12 -IF "%VS120COMNTOOLS%"=="" ( - set COMPILER12="%ProgramFiles%\Microsoft Visual Studio 12.0\Common7\IDE\devenv.com" -) ELSE IF EXIST "%VS120COMNTOOLS%\..\IDE\VCExpress.exe" ( - set COMPILER12="%VS120COMNTOOLS%\..\IDE\VCExpress.exe" -) ELSE IF EXIST "%VS120COMNTOOLS%\..\IDE\devenv.com" ( - set COMPILER12="%VS120COMNTOOLS%\..\IDE\devenv.com" -) ELSE GOTO NOSDK11 +rem Check for VC14 +IF "%VS140COMNTOOLS%"=="" ( + set COMPILER14="%ProgramFiles%\Microsoft Visual Studio 14.0\Common7\IDE\devenv.com" +) ELSE IF EXIST "%VS140COMNTOOLS%\..\IDE\VCExpress.exe" ( + set COMPILER14="%VS140COMNTOOLS%\..\IDE\VCExpress.exe" +) ELSE IF EXIST "%VS140COMNTOOLS%\..\IDE\devenv.com" ( + set COMPILER14="%VS140COMNTOOLS%\..\IDE\devenv.com" +) ELSE GOTO NOSDK14 rmdir /s /q %MYDIR%..\build 2> nul call %MYDIR%build.cmd @@ -39,13 +39,13 @@ if "%PROCESSOR_ARCHITECTURE%"=="x86" if "%PROCESSOR_ARCHITEW6432%"=="" goto libc rem Compile libCEC and cec-client x64 echo. Cleaning libCEC (x64) -%COMPILER12% libcec.sln /Clean "Release|x64" +%COMPILER14% libcec.sln /Clean "Release|x64" echo. Compiling libCEC (x64) -%COMPILER12% libcec.sln /Build "Release|x64" /Project LibCecSharp -%COMPILER12% libcec.sln /Build "Release|x64" +%COMPILER14% libcec.sln /Build "Release|x64" /Project LibCecSharp +%COMPILER14% libcec.sln /Build "Release|x64" echo. Compiling .Net applications cd "%MYDIR%..\src\dotnet\project" -%COMPILER12% cec-dotnet.sln /Build "Release|x64" +%COMPILER14% cec-dotnet.sln /Build "Release|x64" copy ..\build\x64\CecSharpTester.exe %MYDIR%..\build\amd64\CecSharpTester.exe copy ..\build\x64\cec-tray.exe %MYDIR%..\build\amd64\cec-tray.exe @@ -53,13 +53,13 @@ copy ..\build\x64\cec-tray.exe %MYDIR%..\build\amd64\cec-tray.exe rem Compile libCEC and cec-client Win32 cd "%MYDIR%..\project" echo. Cleaning libCEC (x86) -%COMPILER12% libcec.sln /Clean "Release|x86" +%COMPILER14% libcec.sln /Clean "Release|x86" echo. Compiling libCEC (x86) -%COMPILER12% libcec.sln /Build "Release|x86" /Project LibCecSharp -%COMPILER12% libcec.sln /Build "Release|x86" +%COMPILER14% libcec.sln /Build "Release|x86" /Project LibCecSharp +%COMPILER14% libcec.sln /Build "Release|x86" echo. Compiling .Net applications cd "%MYDIR%..\src\dotnet\project" -%COMPILER12% cec-dotnet.sln /Build "Release|x86" +%COMPILER14% cec-dotnet.sln /Build "Release|x86" copy ..\build\x86\CecSharpTester.exe %MYDIR%..\build\x86\CecSharpTester.exe copy ..\build\x86\cec-tray.exe %MYDIR%..\build\x86\cec-tray.exe cd "%MYDIR%..\project" @@ -68,6 +68,9 @@ rem Clean things up before creating the installer del /q /f %MYDIR%..\build\x86\LibCecSharp.pdb 2> nul del /q /f %MYDIR%..\build\amd64\LibCecSharp.pdb 2> nul +GOTO CREATEEGPLUGIN + +:SIGNBINARIES rem Check for sign-binary.cmd, only present on the Pulse-Eight production build system rem Calls signtool.exe and signs the DLLs with Pulse-Eight's code signing key IF NOT EXIST "..\support\private\sign-binary.cmd" GOTO CREATEINSTALLER @@ -88,7 +91,6 @@ CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\amd64\CecSharpTester.exe :CREATEINSTALLER echo. Creating the installer -GOTO CREATEEGPLUGIN cd %MYDIR%..\build\x86 copy cec.dll libcec.dll cd ..\amd64 @@ -110,32 +112,31 @@ IF EXIST "..\support\private\sign-binary.cmd" ( echo. The installer can be found here: %INSTALLER% set EXITCODE=0 -GOTO EGPLUGINCLEANUP +del /q /f %EGSOURCES%..\pulse_eight.egplugin 2> nul GOTO EXIT :CREATEEGPLUGIN echo. Creating EventGhost plugin file SET EGSOURCES=%MYDIR%..\src\eventghost\egplugin_sources\ copy %MYDIR%..\build\x86\python\cec\__init__.py %EGSOURCES%PulseEight\cec -copy %MYDIR%..\build\x86\python\_cec.pyd %EGSOURCES%PulseEight +copy %MYDIR%..\build\x86\python\cec\_cec.pyd %EGSOURCES%PulseEight copy %MYDIR%..\build\x86\cec.dll %EGSOURCES%PulseEight +del /q /f %EGSOURCES%..\pulse_eight.egplugin 2> nul PowerShell -ExecutionPolicy ByPass -Command "Add-Type -Assembly System.IO.Compression.FileSystem;[System.IO.Compression.ZipFile]::CreateFromDirectory('%EGSOURCES%', '%EGSOURCES%..\pulse_eight.egplugin', [System.IO.Compression.CompressionLevel]::Optimal, $false)" -del %EGSOURCES%PulseEight\cec\__init__.py -del %EGSOURCES%PulseEight\_cec.pyd -del %EGSOURCES%PulseEight\cec.dll -IF NOT EXIST "%EGSOURCES%..\pulse_eight.egplugin"( +del /q /f %EGSOURCES%PulseEight\cec\__init__.py 2> nul +del /q /f %EGSOURCES%PulseEight\_cec.pyd 2> nul +del /q /f %EGSOURCES%PulseEight\cec.dll 2> nul +IF NOT EXIST "%EGSOURCES%..\pulse_eight.egplugin" ( GOTO NOEGPLUGIN ) - -:EGPLUGINCLEANUP -del %EGSOURCES%..\pulse_eight.egplugin +GOTO SIGNBINARIES :NOEGPLUGIN echo. Failed to create the EventGhost plugin file. GOTO EXIT -:NOSDK11 -echo. Visual Studio 2012 was not found on your system. +:NOSDK14 +echo. Visual Studio 2015 was not found on your system. GOTO EXIT :NOSIS @@ -148,10 +149,9 @@ GOTO EXIT :ERRORCREATINGINSTALLER echo. The installer could not be created. The most likely cause is that something went wrong while compiling. -GOTO RETURNEXIT :EXIT -cd %MYDIR% +cd %MYDIR%.. :RETURNEXIT exit /b %EXITCODE% diff --git a/windows/visual-studio.cmd b/windows/visual-studio.cmd index 4b670493..efe3d9cd 100644 --- a/windows/visual-studio.cmd +++ b/windows/visual-studio.cmd @@ -6,7 +6,7 @@ SETLOCAL SET MYDIR=%~dp0 SET BUILDTYPE=Debug -SET VSVERSION=12 +SET VSVERSION=14 SET INSTALLPATH=%MYDIR%..\build IF NOT EXIST "%MYDIR%..\support\windows\cmake\build.cmd" ( From d57d3ac91a2e6c7f224082fc749331210718a08b Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Thu, 8 Nov 2018 18:06:17 +0100 Subject: [PATCH 45/93] bump to 4.0.3 --- CMakeLists.txt | 2 +- debian/changelog.in | 48 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 861ba19a..4dc48362 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8.9) set(LIBCEC_VERSION_MAJOR 4) set(LIBCEC_VERSION_MINOR 0) -set(LIBCEC_VERSION_PATCH 2) +set(LIBCEC_VERSION_PATCH 3) # cec-client add_subdirectory(src/cec-client) diff --git a/debian/changelog.in b/debian/changelog.in index b1d11c57..fb7d4165 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,51 @@ +libcec (4.0.3.1~#DIST#) #DIST#; urgency=medium + + * fixed: + * detect debian based distros properly when installing python. closes #314 + * don't filter out broadcast in HandleDeviceVendorCommandWithId(). issue + #309 + * send an active source message when a routing change has been received with + libCEC's address as new route and no active source message has been sent + yet. issue #309 #205 #233 + * vs2015 c++ redistributables + * fix missing tinfo linking in cec-client. #341 + * set wrong variable. #343 #352 + * LG - don't activate the source when receiving vendor command 0xB. #344 + * LG TV always changing input when turned on #307 + * Fix menu language string. #360 + * correct python lib path for python 2.7+/3+. #356 + * Fix build if tinfo library is not present. #398 + * disable autonomous mode when a Samsung TV is connected. Samsung 2017+ TVs + will power on randomly (Samsung bug). don't poll the TV. #424 + * CRPiCECAdapterMessageQueue::Write() accessed the queue without locking. + #423 + * use sig_atomic_t for shared object between sig handler and main. #425 + * python 3.5+ import. #356 + * TDA995x: Fix logical address readback. #303 + * TDA995x: Handle physical address change, optimize logical address setup. + #303 + * Pulse-Eight USB CEC adapter detection on macOS Mojave. #434 + * Fix broken Python version check and failure to build on cmake < 3.7. #409 + * 13 char device name got truncated + + * changed: + * log a warning when we detect that RPi's CEC service is used by something + else, blocking libCEC. issue #191 + * const IAdapterCommunication::GetLogicalAddresses(), making the mutex + mutable for now without changing the platform lib. closes #259 + * Add a Reinitialze action. Useful for after the device has been powered + down. #299 + * Panasonic media control info to the readme + * detect WIN64 in cmake automatically. #322 + * README.developers.md. #330 + * instructions for hdmi_force_hotplug=1 on the pi + * eventghost plugin install/create. #375 + * Include C version of libCEC loader when installing. #397 + * Explicitly use python3 in pyCecClient. #433 + * build with vs 2015 + + -- Pulse-Eight Packaging Thu, 8 Nov 2018 18:05:36 +0100 + libcec (4.0.2.1~#DIST#) #DIST#; urgency=medium * fixed: From f7593243377687c84c27b4e0342b00bb2ef2efbc Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Mon, 3 Dec 2018 15:36:47 +0100 Subject: [PATCH 46/93] fixed: only prevent TV polls when a Samsung TV is detected instead of suppressing all logical addresses. issue #424 #444 --- src/libcec/devices/CECBusDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/devices/CECBusDevice.cpp b/src/libcec/devices/CECBusDevice.cpp index 640487ef..c096807f 100644 --- a/src/libcec/devices/CECBusDevice.cpp +++ b/src/libcec/devices/CECBusDevice.cpp @@ -845,7 +845,7 @@ cec_bus_device_status CCECBusDevice::GetStatus(bool bForcePoll /* = false */, bo bNeedsPoll = !bSuppressPoll && m_deviceStatus != CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC && // don't poll Samsung TVs because they can power on randomly - (m_processor->GetDevice(CECDEVICE_TV)->GetCurrentVendorId() != CEC_VENDOR_SAMSUNG) && + (m_processor->GetDevice(CECDEVICE_TV)->GetCurrentVendorId() != CEC_VENDOR_SAMSUNG || m_iLogicalAddress != CECDEVICE_TV) && // poll forced (bForcePoll || // don't know the status From 3bbd4321618503d14008387a72fabb6743878831 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Fri, 21 Dec 2018 22:34:45 +0100 Subject: [PATCH 47/93] bump to 4.0.4 --- CMakeLists.txt | 2 +- debian/changelog.in | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dc48362..61e0bc70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8.9) set(LIBCEC_VERSION_MAJOR 4) set(LIBCEC_VERSION_MINOR 0) -set(LIBCEC_VERSION_PATCH 3) +set(LIBCEC_VERSION_PATCH 4) # cec-client add_subdirectory(src/cec-client) diff --git a/debian/changelog.in b/debian/changelog.in index fb7d4165..0d72f2de 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,10 @@ +libcec (4.0.4.1~#DIST#) #DIST#; urgency=medium + + * fixed: only prevent TV polls when a Samsung TV is detected instead of + suppressing all logical addresses. issue #424 #444 + + -- Pulse-Eight Packaging Fri, 21 Dec 2018 22:34:14 +0100 + libcec (4.0.3.1~#DIST#) #DIST#; urgency=medium * fixed: From ba9b538abfa6dad351055711549b2bc3e1c4b3c0 Mon Sep 17 00:00:00 2001 From: Martin Ellis Date: Tue, 26 Mar 2019 22:30:23 +0000 Subject: [PATCH 48/93] Update README.developers.md --- docs/README.developers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.developers.md b/docs/README.developers.md index a5678f9b..aa08d867 100644 --- a/docs/README.developers.md +++ b/docs/README.developers.md @@ -21,4 +21,4 @@ We provide a C, C++, Python and .NET CLR interface to the adapter. # Developers Agreement If you wish to contribute to this project, you must first sign our contributors agreement. -Please see [the contributors agreement] (http://www.pulse-eight.net/contributors) for more information. +Please see [the contributors agreement] (http://www.pulse-eight.com/contributors) for more information. From c2ff5ba262694a4e0512e6f7a4a71d2057ef8558 Mon Sep 17 00:00:00 2001 From: Andrew Dawson Date: Mon, 30 Sep 2019 11:27:00 -0400 Subject: [PATCH 49/93] Fixes windows 64-bit not being detected under src/libcec. --- src/libcec/cmake/CheckPlatformSupport.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index 2d7102f3..e1eacf06 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -44,7 +44,8 @@ if(WIN32) add_definitions(-DTARGET_WINDOWS -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -D_WINSOCKAPI_) set(LIB_DESTINATION ".") check_symbol_exists(_X64_ Windows.h WIN64) - if (${WIN64}) + check_symbol_exists(_AMD64_ Windows.h AMD64) + if (DEFINED WIN64 OR DEFINED AMD64) set(LIB_INFO "${LIB_INFO} (x64)") else() add_definitions(-D_USE_32BIT_TIME_T) From 3387ea6865256c1858dae277c1bc18fdd5fe9975 Mon Sep 17 00:00:00 2001 From: Andrew Dawson Date: Fri, 4 Oct 2019 10:59:19 -0400 Subject: [PATCH 50/93] Fixes paths not allowing spaces. --- windows/build-lib.cmd | 10 +++++----- windows/build.cmd | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/windows/build-lib.cmd b/windows/build-lib.cmd index d8a3f705..026f6264 100644 --- a/windows/build-lib.cmd +++ b/windows/build-lib.cmd @@ -8,7 +8,7 @@ SET MYDIR=%~dp0 SET BUILDARCH=%1 SET BUILDTYPE=%2 SET VSVERSION=%3 -SET INSTALLPATH=%4 +SET INSTALLPATH=%~4 SET GENTYPE=%5 IF [%5] == [] GOTO missingparams @@ -21,13 +21,13 @@ IF NOT EXIST "%MYDIR%..\src\platform\windows\build.cmd" ( ) ECHO Build platform library for %BUILDARCH% -CALL %MYDIR%..\src\platform\windows\build-lib.cmd %BUILDARCH% %BUILDTYPE% %VSVERSION% %INSTALLPATH% -del /s /f /q %BUILDTARGET% +CALL "%MYDIR%..\src\platform\windows\build-lib.cmd" %BUILDARCH% %BUILDTYPE% %VSVERSION% "%INSTALLPATH%" +del /s /f /q "%BUILDTARGET%" ECHO Build libCEC for %BUILDARCH% -CALL %MYDIR%..\support\windows\cmake\generate.cmd %BUILDARCH% %GENTYPE% %MYDIR%..\ %BUILDTARGET% %TARGET% %BUILDTYPE% %VSVERSION% +CALL "%MYDIR%..\support\windows\cmake\generate.cmd" %BUILDARCH% %GENTYPE% "%MYDIR%.." "%BUILDTARGET%" "%TARGET%" %BUILDTYPE% %VSVERSION% IF "%GENTYPE%" == "nmake" ( - CALL %MYDIR%..\support\windows\cmake\build.cmd %BUILDARCH% %BUILDTARGET% %VSVERSION% + CALL "%MYDIR%..\support\windows\cmake\build.cmd" %BUILDARCH% "%BUILDTARGET%" %VSVERSION% IF NOT EXIST "%TARGET%\cec.dll" ( echo "Failed to build %TARGET%\cec.dll" exit /b 1 diff --git a/windows/build.cmd b/windows/build.cmd index 7dc42c96..23a405b9 100644 --- a/windows/build.cmd +++ b/windows/build.cmd @@ -6,7 +6,7 @@ SETLOCAL SET MYDIR=%~dp0 SET BUILDTYPE=Release -SET VSVERSION=14 +SET VSVERSION=16 SET INSTALLPATH=%MYDIR%..\build IF NOT EXIST "%MYDIR%..\support\windows\cmake\build.cmd" ( @@ -19,17 +19,17 @@ IF NOT EXIST "%MYDIR%..\src\platform\windows\build.cmd" ( GOTO exit ) -rmdir /s /q %MYDIR%..\build 2> nul +rmdir /s /q "%MYDIR%..\build" 2> nul FOR %%T IN (amd64 x86) DO ( - echo Run %MYDIR%build-lib.cmd %%T - CALL %MYDIR%build-lib.cmd %%T %BUILDTYPE% %VSVERSION% %INSTALLPATH% nmake + echo Run "%MYDIR%build-lib.cmd" %%T + CALL "%MYDIR%build-lib.cmd" %%T %BUILDTYPE% %VSVERSION% "%INSTALLPATH%" nmake IF NOT ERRORLEVEL 0 ( GOTO builderror ) ) -rmdir /s /q %MYDIR%..\build\cmake 2> nul +rmdir /s /q "%MYDIR%..\build\cmake" 2> nul exit /b 0 :builderror From 9d88f8d48e56ead6dfd0309e57b5f778be1ab024 Mon Sep 17 00:00:00 2001 From: fritsch Date: Sun, 15 Mar 2020 20:54:55 +0100 Subject: [PATCH 51/93] USBCECAdapterDetection: Only scan tty --- src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp index d3efc193..d669b754 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp @@ -356,6 +356,8 @@ uint8_t CUSBCECAdapterDetection::FindAdaptersUdev(cec_adapter_descriptor *device struct udev_list_entry *devices, *dev_list_entry; struct udev_device *dev, *pdev; enumerate = udev_enumerate_new(udev); + + udev_enumerate_add_match_subsystem(enumerate, "tty"); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(dev_list_entry, devices) From 5e5788de12ba4f69ee3afad19e6c0e4b45f0ddf4 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Fri, 27 Mar 2020 15:29:40 +0100 Subject: [PATCH 52/93] fixed: don't probe all devices in CCECClient::IsActiveDeviceType(). closes #492 --- src/libcec/CECClient.cpp | 6 +----- src/libcec/devices/CECBusDevice.cpp | 7 +++++++ src/libcec/devices/CECBusDevice.h | 1 + src/libcec/devices/CECDeviceMap.cpp | 24 +++++++++++++++++++----- src/libcec/devices/CECDeviceMap.h | 1 + 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/libcec/CECClient.cpp b/src/libcec/CECClient.cpp index bb8b0cd9..a8efd99d 100644 --- a/src/libcec/CECClient.cpp +++ b/src/libcec/CECClient.cpp @@ -1457,11 +1457,7 @@ bool CCECClient::IsActiveDevice(const cec_logical_address iAddress) bool CCECClient::IsActiveDeviceType(const cec_device_type type) { - CECDEVICEVEC activeDevices; - if (m_processor) - m_processor->GetDevices()->GetActive(activeDevices); - CCECDeviceMap::FilterType(type, activeDevices); - return !activeDevices.empty(); + return m_processor->GetDevices()->IsActiveType(type); } cec_logical_address CCECClient::GetActiveSource(void) diff --git a/src/libcec/devices/CECBusDevice.cpp b/src/libcec/devices/CECBusDevice.cpp index c096807f..a0bd183a 100644 --- a/src/libcec/devices/CECBusDevice.cpp +++ b/src/libcec/devices/CECBusDevice.cpp @@ -301,6 +301,13 @@ bool CCECBusDevice::IsHandledByLibCEC(void) return m_deviceStatus == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC; } +bool CCECBusDevice::IsActive(void) +{ + CLockObject lock(m_mutex); + return (m_deviceStatus == CEC_DEVICE_STATUS_PRESENT) || + (m_deviceStatus == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC); +} + void CCECBusDevice::SetUnsupportedFeature(cec_opcode opcode) { // some commands should never be marked as unsupported diff --git a/src/libcec/devices/CECBusDevice.h b/src/libcec/devices/CECBusDevice.h index ac825961..89e51861 100644 --- a/src/libcec/devices/CECBusDevice.h +++ b/src/libcec/devices/CECBusDevice.h @@ -111,6 +111,7 @@ namespace CEC virtual const char* GetLogicalAddressName(void) const; virtual bool IsPresent(void); virtual bool IsHandledByLibCEC(void); + virtual bool IsActive(void); virtual bool HandleCommand(const cec_command &command); virtual bool IsUnsupportedFeature(cec_opcode opcode); diff --git a/src/libcec/devices/CECDeviceMap.cpp b/src/libcec/devices/CECDeviceMap.cpp index 4b515924..884a61dc 100644 --- a/src/libcec/devices/CECDeviceMap.cpp +++ b/src/libcec/devices/CECDeviceMap.cpp @@ -175,13 +175,27 @@ void CCECDeviceMap::GetLibCECControlled(CECDEVICEVEC &devices) const void CCECDeviceMap::GetActive(CECDEVICEVEC &devices) const { - for (CECDEVICEMAP::const_iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++) + for (auto it = m_busDevices.begin(); it != m_busDevices.end(); ++it) { - cec_bus_device_status status = it->second->GetStatus(); - if (status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC || - status == CEC_DEVICE_STATUS_PRESENT) - devices.push_back(it->second); + auto dev = it->second; + if (!!dev && dev->IsActive()) { + devices.push_back(it->second); + } + } +} + +bool CCECDeviceMap::IsActiveType(const cec_device_type type) const +{ + for (auto it = m_busDevices.begin(); it != m_busDevices.end(); ++it) + { + auto dev = it->second; + if (!!dev && + (dev->GetType() == type) && + (dev->IsActive())) { + return true; + } } + return false; } void CCECDeviceMap::GetPowerOffDevices(const libcec_configuration &configuration, CECDEVICEVEC &devices) const diff --git a/src/libcec/devices/CECDeviceMap.h b/src/libcec/devices/CECDeviceMap.h index 34a1da27..ca2f029a 100644 --- a/src/libcec/devices/CECDeviceMap.h +++ b/src/libcec/devices/CECDeviceMap.h @@ -63,6 +63,7 @@ namespace CEC void GetLibCECControlled(CECDEVICEVEC &devices) const; void GetByLogicalAddresses(CECDEVICEVEC &devices, const cec_logical_addresses &addresses); void GetActive(CECDEVICEVEC &devices) const; + bool IsActiveType(const cec_device_type type) const; void GetByType(const cec_device_type type, CECDEVICEVEC &devices) const; void GetChildrenOf(CECDEVICEVEC& devices, CCECBusDevice* device) const; void SignalAll(cec_opcode opcode); From d04037825e8c49075ddd3ae6f8f0b94a0fef2709 Mon Sep 17 00:00:00 2001 From: "Bernhard M. Wiedemann" Date: Tue, 17 Dec 2019 10:48:08 +0100 Subject: [PATCH 53/93] Use cmake TIMESTAMP function to be more portable and to allow for reproducible builds See https://reproducible-builds.org/ for why this matters. Also consistently use ISO 8601 date format to be understood everywhere. Fixes #485 --- CMakeLists.txt | 2 +- src/libcec/cmake/SetBuildInfo.cmake | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61e0bc70..f1ebaea5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ project(libcec) -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 2.8.11) set(LIBCEC_VERSION_MAJOR 4) set(LIBCEC_VERSION_MINOR 0) diff --git a/src/libcec/cmake/SetBuildInfo.cmake b/src/libcec/cmake/SetBuildInfo.cmake index 39bf4be0..9e1b3ee2 100644 --- a/src/libcec/cmake/SetBuildInfo.cmake +++ b/src/libcec/cmake/SetBuildInfo.cmake @@ -24,13 +24,8 @@ else() endif() # add compilation date to compile info - find_program(HAVE_DATE_BIN date /bin /usr/bin /usr/local/bin) - if(HAVE_DATE_BIN) - exec_program(date ARGS -u OUTPUT_VARIABLE BUILD_DATE) - set(LIB_INFO "${LIB_INFO} compiled on ${BUILD_DATE}") - else() - set(LIB_INFO "${LIB_INFO} compiled on (unknown date)") - endif() + STRING(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S" UTC) + set(LIB_INFO "${LIB_INFO} compiled on ${BUILD_DATE}") # add user who built this to compile info find_program(HAVE_WHOAMI_BIN whoami /bin /usr/bin /usr/local/bin) From 59ec4ddf40e9bbeaebe82ea26427dac6f8457581 Mon Sep 17 00:00:00 2001 From: Igor Nikolaev Date: Sun, 15 Dec 2019 13:36:08 +0200 Subject: [PATCH 54/93] Add Apple vendor ID --- include/cectypes.h | 1 + src/LibCecSharp/CecSharpTypes.h | 1 + src/libcec/CECTypeUtils.h | 2 ++ 3 files changed, 4 insertions(+) diff --git a/include/cectypes.h b/include/cectypes.h index 9c918427..a4b99d8e 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -831,6 +831,7 @@ typedef enum cec_vendor_id CEC_VENDOR_ONKYO = 0x0009B0, CEC_VENDOR_MEDION = 0x000CB8, CEC_VENDOR_TOSHIBA2 = 0x000CE7, + CEC_VENDOR_APPLE = 0x0010FA, CEC_VENDOR_PULSE_EIGHT = 0x001582, CEC_VENDOR_HARMAN_KARDON2 = 0x001950, CEC_VENDOR_GOOGLE = 0x001A11, diff --git a/src/LibCecSharp/CecSharpTypes.h b/src/LibCecSharp/CecSharpTypes.h index 434e1a8d..8af13278 100644 --- a/src/LibCecSharp/CecSharpTypes.h +++ b/src/LibCecSharp/CecSharpTypes.h @@ -792,6 +792,7 @@ namespace CecSharp Onkyo = 0x0009B0, Medion = 0x000CB8, Toshiba2 = 0x000CE7, + Apple = 0x0010FA, PulseEight = 0x001582, HarmanKardon2 = 0x001950, Google = 0x001A11, diff --git a/src/libcec/CECTypeUtils.h b/src/libcec/CECTypeUtils.h index 25c1c6e3..eeb07fbe 100644 --- a/src/libcec/CECTypeUtils.h +++ b/src/libcec/CECTypeUtils.h @@ -497,6 +497,8 @@ namespace CEC case CEC_VENDOR_TOSHIBA: case CEC_VENDOR_TOSHIBA2: return "Toshiba"; + case CEC_VENDOR_APPLE: + return "Apple"; case CEC_VENDOR_AKAI: return "Akai"; case CEC_VENDOR_AOC: From 03992b52f105fadc09325f08ecebeb9b00db900c Mon Sep 17 00:00:00 2001 From: Gabriel Francu Date: Sun, 20 Oct 2019 17:56:55 +0300 Subject: [PATCH 55/93] correct 'python3-dev' package name --- docs/README.raspberrypi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.raspberrypi.md b/docs/README.raspberrypi.md index d8806b70..a50be085 100644 --- a/docs/README.raspberrypi.md +++ b/docs/README.raspberrypi.md @@ -21,7 +21,7 @@ cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/CrossCompile.cmake \ To compile libCEC on a new Raspbian installation, follow these instructions: ``` sudo apt-get update -sudo apt-get install cmake libudev-dev libxrandr-dev python-dev swig +sudo apt-get install cmake libudev-dev libxrandr-dev python3-dev swig cd git clone https://github.com/Pulse-Eight/platform.git mkdir platform/build From 81c4bc5e455e776f2b0b149b41f5a1c349a57359 Mon Sep 17 00:00:00 2001 From: Gabriel Francu Date: Sun, 20 Oct 2019 17:58:41 +0300 Subject: [PATCH 56/93] fix Python3 function name --- src/pyCecClient/pyCecClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyCecClient/pyCecClient.py b/src/pyCecClient/pyCecClient.py index d0504ac6..8b18d88b 100755 --- a/src/pyCecClient/pyCecClient.py +++ b/src/pyCecClient/pyCecClient.py @@ -149,7 +149,7 @@ def ProcessCommandScan(self): def MainLoop(self): runLoop = True while runLoop: - command = raw_input("Enter command:").lower() + command = input("Enter command:").lower() if command == 'q' or command == 'quit': runLoop = False elif command == 'self': From 10477e02432449d26d8d709505e9e95055cd212d Mon Sep 17 00:00:00 2001 From: Matthew Hitchens Date: Sun, 6 Oct 2019 02:25:19 -0500 Subject: [PATCH 57/93] Adding O_CLOEXEC to prevent child process locking The serial port should be closed if a child process successfully invokes execve, as failure to do so could result in a child process holding an exclusive lock to the serial port. --- src/libcec/platform/posix/serialport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/platform/posix/serialport.cpp b/src/libcec/platform/posix/serialport.cpp index c97bd69f..ab47852c 100644 --- a/src/libcec/platform/posix/serialport.cpp +++ b/src/libcec/platform/posix/serialport.cpp @@ -131,7 +131,7 @@ bool CSerialSocket::Open(uint64_t iTimeoutMs /* = 0 */) return false; } - m_socket = open(m_strName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); + m_socket = open(m_strName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY | O_CLOEXEC); if (m_socket == INVALID_SERIAL_SOCKET_VALUE) { From 3df5de7a3aefbb2c90a67ef281f7a618f266615d Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Fri, 27 Mar 2020 17:11:02 +0100 Subject: [PATCH 58/93] fixed: don't change vs versions (yet) --- windows/build.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/build.cmd b/windows/build.cmd index 23a405b9..d20cb676 100644 --- a/windows/build.cmd +++ b/windows/build.cmd @@ -6,7 +6,7 @@ SETLOCAL SET MYDIR=%~dp0 SET BUILDTYPE=Release -SET VSVERSION=16 +SET VSVERSION=14 SET INSTALLPATH=%MYDIR%..\build IF NOT EXIST "%MYDIR%..\support\windows\cmake\build.cmd" ( From 70d71cb16fa7c334373f3ba0148b89eafc04f73f Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Wed, 25 Sep 2019 14:53:48 +0200 Subject: [PATCH 59/93] added: P8 adapter discovery via Linux sysfs On Linux, dynamic device discovery is currently implemented via libudev, which may not be available on more minimal systems. Thus, we implement a new device discovery that directly uses sysfs to scan through available USB devices for supported ones without any additional dependencies. --- .../Pulse-Eight/USBCECAdapterDetection.cpp | 75 ++++++++++++++++++- .../Pulse-Eight/USBCECAdapterDetection.h | 1 + 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp index d669b754..a97e48dc 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp @@ -68,6 +68,12 @@ extern "C" { #include #endif +#if defined(__linux__) +#include +#include +#include +#endif + #include #include #include @@ -79,7 +85,7 @@ extern "C" { using namespace CEC; -#if defined(HAVE_LIBUDEV) +#if defined(HAVE_LIBUDEV) || defined(__linux__) bool TranslateComPort(std::string& strString) { std::string strTmp(strString); @@ -130,7 +136,7 @@ bool FindComPort(std::string& strLocation) bool CUSBCECAdapterDetection::CanAutodetect(void) { -#if defined(__APPLE__) || defined(HAVE_LIBUDEV) || defined(__WINDOWS__) || defined(__FreeBSD__) +#if defined(__APPLE__) || defined(HAVE_LIBUDEV) || defined(__WINDOWS__) || defined(__FreeBSD__) || defined(__linux__) return true; #else return false; @@ -413,6 +419,69 @@ uint8_t CUSBCECAdapterDetection::FindAdaptersUdev(cec_adapter_descriptor *device return iFound; } +uint8_t CUSBCECAdapterDetection::FindAdaptersLinux(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) +{ + uint8_t iFound(0); + +#if defined(__linux__) + std::string strSysfsPath("/sys/bus/usb/devices"); + DIR *dir; + + if ((dir = opendir(strSysfsPath.c_str())) != NULL) + { + struct dirent *dent; + + while ((dent = readdir(dir)) != NULL) + { + std::string strDevice = StringUtils::Format("%s/%s", strSysfsPath.c_str(), dent->d_name); + unsigned int iVendor, iProduct; + + if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) + continue; + + std::ifstream fVendor(StringUtils::Format("%s/idVendor", strDevice.c_str())); + if (!fVendor) + continue; + fVendor >> std::hex >> iVendor; + + std::ifstream fProduct(StringUtils::Format("%s/idProduct", strDevice.c_str())); + if (!fProduct) + continue; + fProduct >> std::hex >> iProduct; + + if (iVendor != CEC_VID || (iProduct != CEC_PID && iProduct != CEC_PID2)) + continue; + + if (strDevicePath && strcmp(strDevice.c_str(), strDevicePath)) + continue; + + std::string strPort(strDevice); + if (FindComPort(strPort) && (iFound == 0 || strcmp(deviceList[iFound - 1].strComName, strPort.c_str()))) + { + snprintf(deviceList[iFound].strComPath, sizeof(deviceList[iFound].strComPath), "%s", strDevice.c_str()); + snprintf(deviceList[iFound].strComName, sizeof(deviceList[iFound].strComName), "%s", strPort.c_str()); + deviceList[iFound].iVendorId = iVendor; + deviceList[iFound].iProductId = iProduct; + deviceList[iFound].adapterType = ADAPTERTYPE_P8_EXTERNAL; // will be overridden when not doing a "quick scan" by the actual type + iFound++; + } + + if (iFound >= iBufSize) + break; + } + + closedir(dir); + } + +#else + (void)deviceList; + (void)iBufSize; + (void)strDevicePath; +#endif + + return iFound; +} + uint8_t CUSBCECAdapterDetection::FindAdaptersFreeBSD(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) { uint8_t iFound(0); @@ -508,6 +577,8 @@ uint8_t CUSBCECAdapterDetection::FindAdapters(cec_adapter_descriptor *deviceList iFound = FindAdaptersFreeBSD(deviceList, iBufSize, strDevicePath); if (iFound == 0) iFound = FindAdaptersUdev(deviceList, iBufSize, strDevicePath); + if (iFound == 0) + iFound = FindAdaptersLinux(deviceList, iBufSize, strDevicePath); if (iFound == 0) iFound = FindAdaptersWindows(deviceList, iBufSize, strDevicePath); return iFound; diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.h index ddc0c944..0d6cafb1 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.h @@ -50,6 +50,7 @@ namespace CEC static uint8_t FindAdaptersWindows(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); static uint8_t FindAdaptersApple(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); static uint8_t FindAdaptersUdev(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); + static uint8_t FindAdaptersLinux(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); static uint8_t FindAdaptersFreeBSD(cec_adapter_descriptor *deviceList, uint8_t iBufSize, const char *strDevicePath = NULL); }; }; From 9b1e201430079f3ab93d28071f0592e291605a9f Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 26 Sep 2019 07:39:24 +0200 Subject: [PATCH 60/93] fixed: fix build if hostname does not support '-f' argument Part of the build information compiled into the library is the host on which it was built. This information is generated by executing the command `hostname -f`, but the '-f' argument may not be available on all platforms (e.g. coreutils v8.30 does not have it). As a result, the command will spit an error message that spans multiple lines, break the format of the define in "src/libcec/env.h" and as a result the error will fail due to invalid syntax. Fix the issue by falling back to just `hostname` if `hostname -f` returns an error. --- src/libcec/cmake/SetBuildInfo.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libcec/cmake/SetBuildInfo.cmake b/src/libcec/cmake/SetBuildInfo.cmake index 9e1b3ee2..839228ef 100644 --- a/src/libcec/cmake/SetBuildInfo.cmake +++ b/src/libcec/cmake/SetBuildInfo.cmake @@ -40,7 +40,10 @@ else() # add host on which this was built to compile info find_program(HAVE_HOSTNAME_BIN hostname /bin /usr/bin /usr/local/bin) if(HAVE_HOSTNAME_BIN) - exec_program(hostname ARGS -f OUTPUT_VARIABLE BUILD_HOST) + exec_program(hostname ARGS -f OUTPUT_VARIABLE BUILD_HOST RETURN_VALUE RETURN_HOST) + if (RETURN_HOST) + exec_program(hostname OUTPUT_VARIABLE BUILD_HOST) + endif() set(LIB_INFO "${LIB_INFO}@${BUILD_HOST}") endif() From 277994c87e59a21730217a2b9c34636c6524c400 Mon Sep 17 00:00:00 2001 From: Brendan McShane Date: Tue, 30 Jul 2019 19:45:29 -0400 Subject: [PATCH 61/93] fix readme hyperlink --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a584f09..a1af8b23 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # About This library provides support for Pulse-Eight's USB-CEC adapter and other CEC capable hardware, like the Raspberry Pi. -A list of frequently asked questions can be found on [libCEC's FAQ page] (http://libcec.pulse-eight.com/faq). +A list of frequently asked questions can be found on [libCEC's FAQ page](http://libcec.pulse-eight.com/faq). .Net client applications, previously part of this repository, have been moved to [this repository](https://github.com/Pulse-Eight/cec-dotnet). From be862230aaa68124f3e7ea9bbbb753303f8a504a Mon Sep 17 00:00:00 2001 From: Brendan McShane Date: Tue, 30 Jul 2019 19:47:44 -0400 Subject: [PATCH 62/93] Fix Hyperlink --- docs/README.developers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.developers.md b/docs/README.developers.md index aa08d867..bfbc23c1 100644 --- a/docs/README.developers.md +++ b/docs/README.developers.md @@ -21,4 +21,4 @@ We provide a C, C++, Python and .NET CLR interface to the adapter. # Developers Agreement If you wish to contribute to this project, you must first sign our contributors agreement. -Please see [the contributors agreement] (http://www.pulse-eight.com/contributors) for more information. +Please see [the contributors agreement](http://www.pulse-eight.com/contributors) for more information. From 8d41940cbd2a18cc32ccab3a7387be79b411cf06 Mon Sep 17 00:00:00 2001 From: Brendan McShane Date: Tue, 30 Jul 2019 20:03:10 -0400 Subject: [PATCH 63/93] fix hyperlinks --- docs/README.linux.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/README.linux.md b/docs/README.linux.md index c59fb806..91928923 100644 --- a/docs/README.linux.md +++ b/docs/README.linux.md @@ -2,12 +2,12 @@ ### Prerequisites libCEC needs the following dependencies in order to work correctly: -* [p8-platform] (https://github.com/Pulse-Eight/platform) 2.0 or later +* [p8-platform](https://github.com/Pulse-Eight/platform) 2.0 or later * udev v151 or later * cdc-acm support compiled into the kernel or available as module To compile libCEC on Linux, you'll need the following dependencies: -* [cmake 2.6 or better] (http://www.cmake.org/) +* [cmake 2.6 or better](http://www.cmake.org/) * a supported C++ 11 compiler The following dependencies are recommended. Without them, the adapter can not @@ -52,4 +52,4 @@ cmake -DHAVE_TDA995X_API=1 .. ``` ### Debian / Ubuntu .deb packaging -See [docs/README.debian.md](README.debian.md). \ No newline at end of file +See [docs/README.debian.md](README.debian.md). From c99cad00db37530b2f98166e6370153b53777759 Mon Sep 17 00:00:00 2001 From: Brendan McShane Date: Tue, 30 Jul 2019 20:03:45 -0400 Subject: [PATCH 64/93] fix hyperlinks --- docs/README.osx.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/README.osx.md b/docs/README.osx.md index 6cb2880e..9240febb 100644 --- a/docs/README.osx.md +++ b/docs/README.osx.md @@ -2,15 +2,15 @@ ### MacPorts -libCEC is available through [MacPorts] (https://www.macports.org/) and has been tested on OS X 10.9 through 10.12 +libCEC is available through [MacPorts](https://www.macports.org/) and has been tested on OS X 10.9 through 10.12 ### Prerequisites To compile libCEC on OS X, you'll need the following dependencies: -* [p8-platform] (https://github.com/Pulse-Eight/platform) 2.0 or later -* [cmake 2.6 or better] (http://www.cmake.org/) +* [p8-platform](https://github.com/Pulse-Eight/platform) 2.0 or later +* [cmake 2.6 or better](http://www.cmake.org/) * a supported C++ 11 compiler. Support for C++11 was added in OS X 10.9 * xcode 3.2.6 or later -* optional: [Python 3.4 or later] (https://www.python.org/) and [Swig] (http://www.swig.org/) to generate Python bindings +* optional: [Python 3.4 or later](https://www.python.org/) and [Swig](http://www.swig.org/) to generate Python bindings * optional: libX11 and xrandr to read the sink's EDID, used to determine the PC's HDMI physical address ### Compilation From 4b0a4816febeff88bc4021e14831a394cf797565 Mon Sep 17 00:00:00 2001 From: Brendan McShane Date: Tue, 30 Jul 2019 20:04:23 -0400 Subject: [PATCH 65/93] fix hyperlinks --- docs/README.windows.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/README.windows.md b/docs/README.windows.md index 8375cf85..8bc1e25c 100644 --- a/docs/README.windows.md +++ b/docs/README.windows.md @@ -13,12 +13,12 @@ Example implementations can be found on [Github](https://github.com/Pulse-Eight/ ### Prerequisites To compile libCEC on Windows, you'll need the following dependencies: -* [p8-platform] (https://github.com/Pulse-Eight/platform) 2.0 or later -* [cmake 2.6 or better] (http://www.cmake.org/) -* [Visual Studio 2008 (v90) Professional] (https://www.visualstudio.com/) -* [Visual Studio 2010 (v110) (for x64 builds: Professional)] (https://www.visualstudio.com/) -* [Visual Studio 2013 (v120) or 2015 (v140)] (https://www.visualstudio.com/) -* To create an installer, you'll need [Nullsoft's NSIS] (http://nsis.sourceforge.net/) +* [p8-platform](https://github.com/Pulse-Eight/platform) 2.0 or later +* [cmake 2.6 or better](http://www.cmake.org/) +* [Visual Studio 2008 (v90) Professional](https://www.visualstudio.com/) +* [Visual Studio 2010 (v110) (for x64 builds: Professional)](https://www.visualstudio.com/) +* [Visual Studio 2013 (v120) or 2015 (v140)](https://www.visualstudio.com/) +* To create an installer, you'll need [Nullsoft's NSIS](http://nsis.sourceforge.net/) Visual Studio version must be installed in ascending order, and each version of Visual Studio must be started at least once before the next version is installed. From 622f69f0b9b6b37a667df8a8ebcecda8a02c9d50 Mon Sep 17 00:00:00 2001 From: Hedda Date: Mon, 7 Jan 2019 10:00:38 +0100 Subject: [PATCH 66/93] Update README.md with link to Skull Canyon and Hades Canyon NUC adapter Update README.md with link to Skull Canyon and Hades Canyon NUC CEC adapter. Note! Pulse-Eight has two links for it but they appear to be for the same adapter: * https://www.pulse-eight.com/p/207/skull-canyon-nuc-cec-adapter * https://www.pulse-eight.com/p/208/hades-canyon-nuc-cec-adapter --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a1af8b23..34b4b9b7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # About This library provides support for Pulse-Eight's USB-CEC adapter and other CEC capable hardware, like the Raspberry Pi. -A list of frequently asked questions can be found on [libCEC's FAQ page](http://libcec.pulse-eight.com/faq). +A list of FAQ (Frequently Asked Questions) can be found on [libCEC's FAQ page](http://libcec.pulse-eight.com/faq). .Net client applications, previously part of this repository, have been moved to [this repository](https://github.com/Pulse-Eight/cec-dotnet). @@ -21,6 +21,7 @@ See [docs/README.windows.md](docs/README.windows.md). # Supported hardware * [Pulse-Eight USB-CEC Adapter](https://www.pulse-eight.com/p/104/usb-hdmi-cec-adapter) * [Pulse-Eight Intel NUC CEC Adapter](https://www.pulse-eight.com/p/154/intel-nuc-hdmi-cec-adapter) +* [Pulse-Eight CEC Adapter for Skull Canyon and Hades Canyon NUC systems](https://www.pulse-eight.com/p/207/skull-canyon-nuc-cec-adapter) * [Raspberry Pi](https://www.raspberrypi.org/) * Some Exynos SoCs * NXP TDA995x From 2a05e544756617483e053fd5259626f206cafc36 Mon Sep 17 00:00:00 2001 From: mjmgit1 <36164379+mjmgit1@users.noreply.github.com> Date: Mon, 5 Feb 2018 13:05:13 -0500 Subject: [PATCH 67/93] udpated to include Request/Report SAD messages Added the hex values for and --- include/cectypes.h | 140 +++++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/include/cectypes.h b/include/cectypes.h index a4b99d8e..4c7be0d0 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -728,77 +728,79 @@ typedef enum cec_logical_address typedef enum cec_opcode { - CEC_OPCODE_ACTIVE_SOURCE = 0x82, - CEC_OPCODE_IMAGE_VIEW_ON = 0x04, - CEC_OPCODE_TEXT_VIEW_ON = 0x0D, - CEC_OPCODE_INACTIVE_SOURCE = 0x9D, - CEC_OPCODE_REQUEST_ACTIVE_SOURCE = 0x85, - CEC_OPCODE_ROUTING_CHANGE = 0x80, - CEC_OPCODE_ROUTING_INFORMATION = 0x81, - CEC_OPCODE_SET_STREAM_PATH = 0x86, - CEC_OPCODE_STANDBY = 0x36, - CEC_OPCODE_RECORD_OFF = 0x0B, - CEC_OPCODE_RECORD_ON = 0x09, - CEC_OPCODE_RECORD_STATUS = 0x0A, - CEC_OPCODE_RECORD_TV_SCREEN = 0x0F, - CEC_OPCODE_CLEAR_ANALOGUE_TIMER = 0x33, - CEC_OPCODE_CLEAR_DIGITAL_TIMER = 0x99, - CEC_OPCODE_CLEAR_EXTERNAL_TIMER = 0xA1, - CEC_OPCODE_SET_ANALOGUE_TIMER = 0x34, - CEC_OPCODE_SET_DIGITAL_TIMER = 0x97, - CEC_OPCODE_SET_EXTERNAL_TIMER = 0xA2, - CEC_OPCODE_SET_TIMER_PROGRAM_TITLE = 0x67, - CEC_OPCODE_TIMER_CLEARED_STATUS = 0x43, - CEC_OPCODE_TIMER_STATUS = 0x35, - CEC_OPCODE_CEC_VERSION = 0x9E, - CEC_OPCODE_GET_CEC_VERSION = 0x9F, - CEC_OPCODE_GIVE_PHYSICAL_ADDRESS = 0x83, - CEC_OPCODE_GET_MENU_LANGUAGE = 0x91, - CEC_OPCODE_REPORT_PHYSICAL_ADDRESS = 0x84, - CEC_OPCODE_SET_MENU_LANGUAGE = 0x32, - CEC_OPCODE_DECK_CONTROL = 0x42, - CEC_OPCODE_DECK_STATUS = 0x1B, - CEC_OPCODE_GIVE_DECK_STATUS = 0x1A, - CEC_OPCODE_PLAY = 0x41, - CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS = 0x08, - CEC_OPCODE_SELECT_ANALOGUE_SERVICE = 0x92, - CEC_OPCODE_SELECT_DIGITAL_SERVICE = 0x93, - CEC_OPCODE_TUNER_DEVICE_STATUS = 0x07, - CEC_OPCODE_TUNER_STEP_DECREMENT = 0x06, - CEC_OPCODE_TUNER_STEP_INCREMENT = 0x05, - CEC_OPCODE_DEVICE_VENDOR_ID = 0x87, - CEC_OPCODE_GIVE_DEVICE_VENDOR_ID = 0x8C, - CEC_OPCODE_VENDOR_COMMAND = 0x89, - CEC_OPCODE_VENDOR_COMMAND_WITH_ID = 0xA0, - CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN = 0x8A, - CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP = 0x8B, - CEC_OPCODE_SET_OSD_STRING = 0x64, - CEC_OPCODE_GIVE_OSD_NAME = 0x46, - CEC_OPCODE_SET_OSD_NAME = 0x47, - CEC_OPCODE_MENU_REQUEST = 0x8D, - CEC_OPCODE_MENU_STATUS = 0x8E, - CEC_OPCODE_USER_CONTROL_PRESSED = 0x44, - CEC_OPCODE_USER_CONTROL_RELEASE = 0x45, - CEC_OPCODE_GIVE_DEVICE_POWER_STATUS = 0x8F, - CEC_OPCODE_REPORT_POWER_STATUS = 0x90, - CEC_OPCODE_FEATURE_ABORT = 0x00, - CEC_OPCODE_ABORT = 0xFF, - CEC_OPCODE_GIVE_AUDIO_STATUS = 0x71, - CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D, - CEC_OPCODE_REPORT_AUDIO_STATUS = 0x7A, - CEC_OPCODE_SET_SYSTEM_AUDIO_MODE = 0x72, - CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST = 0x70, - CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS = 0x7E, - CEC_OPCODE_SET_AUDIO_RATE = 0x9A, + CEC_OPCODE_ACTIVE_SOURCE = 0x82, + CEC_OPCODE_IMAGE_VIEW_ON = 0x04, + CEC_OPCODE_TEXT_VIEW_ON = 0x0D, + CEC_OPCODE_INACTIVE_SOURCE = 0x9D, + CEC_OPCODE_REQUEST_ACTIVE_SOURCE = 0x85, + CEC_OPCODE_ROUTING_CHANGE = 0x80, + CEC_OPCODE_ROUTING_INFORMATION = 0x81, + CEC_OPCODE_SET_STREAM_PATH = 0x86, + CEC_OPCODE_STANDBY = 0x36, + CEC_OPCODE_RECORD_OFF = 0x0B, + CEC_OPCODE_RECORD_ON = 0x09, + CEC_OPCODE_RECORD_STATUS = 0x0A, + CEC_OPCODE_RECORD_TV_SCREEN = 0x0F, + CEC_OPCODE_CLEAR_ANALOGUE_TIMER = 0x33, + CEC_OPCODE_CLEAR_DIGITAL_TIMER = 0x99, + CEC_OPCODE_CLEAR_EXTERNAL_TIMER = 0xA1, + CEC_OPCODE_SET_ANALOGUE_TIMER = 0x34, + CEC_OPCODE_SET_DIGITAL_TIMER = 0x97, + CEC_OPCODE_SET_EXTERNAL_TIMER = 0xA2, + CEC_OPCODE_SET_TIMER_PROGRAM_TITLE = 0x67, + CEC_OPCODE_TIMER_CLEARED_STATUS = 0x43, + CEC_OPCODE_TIMER_STATUS = 0x35, + CEC_OPCODE_CEC_VERSION = 0x9E, + CEC_OPCODE_GET_CEC_VERSION = 0x9F, + CEC_OPCODE_GIVE_PHYSICAL_ADDRESS = 0x83, + CEC_OPCODE_GET_MENU_LANGUAGE = 0x91, + CEC_OPCODE_REPORT_PHYSICAL_ADDRESS = 0x84, + CEC_OPCODE_SET_MENU_LANGUAGE = 0x32, + CEC_OPCODE_DECK_CONTROL = 0x42, + CEC_OPCODE_DECK_STATUS = 0x1B, + CEC_OPCODE_GIVE_DECK_STATUS = 0x1A, + CEC_OPCODE_PLAY = 0x41, + CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS = 0x08, + CEC_OPCODE_SELECT_ANALOGUE_SERVICE = 0x92, + CEC_OPCODE_SELECT_DIGITAL_SERVICE = 0x93, + CEC_OPCODE_TUNER_DEVICE_STATUS = 0x07, + CEC_OPCODE_TUNER_STEP_DECREMENT = 0x06, + CEC_OPCODE_TUNER_STEP_INCREMENT = 0x05, + CEC_OPCODE_DEVICE_VENDOR_ID = 0x87, + CEC_OPCODE_GIVE_DEVICE_VENDOR_ID = 0x8C, + CEC_OPCODE_VENDOR_COMMAND = 0x89, + CEC_OPCODE_VENDOR_COMMAND_WITH_ID = 0xA0, + CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN = 0x8A, + CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP = 0x8B, + CEC_OPCODE_SET_OSD_STRING = 0x64, + CEC_OPCODE_GIVE_OSD_NAME = 0x46, + CEC_OPCODE_SET_OSD_NAME = 0x47, + CEC_OPCODE_MENU_REQUEST = 0x8D, + CEC_OPCODE_MENU_STATUS = 0x8E, + CEC_OPCODE_USER_CONTROL_PRESSED = 0x44, + CEC_OPCODE_USER_CONTROL_RELEASE = 0x45, + CEC_OPCODE_GIVE_DEVICE_POWER_STATUS = 0x8F, + CEC_OPCODE_REPORT_POWER_STATUS = 0x90, + CEC_OPCODE_FEATURE_ABORT = 0x00, + CEC_OPCODE_ABORT = 0xFF, + CEC_OPCODE_GIVE_AUDIO_STATUS = 0x71, + CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D, + CEC_OPCODE_REPORT_AUDIO_STATUS = 0x7A, + CEC_OPCODE_SET_SYSTEM_AUDIO_MODE = 0x72, + CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST = 0x70, + CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS = 0x7E, + CEC_OPCODE_SET_AUDIO_RATE = 0x9A, /* CEC 1.4 */ - CEC_OPCODE_START_ARC = 0xC0, - CEC_OPCODE_REPORT_ARC_STARTED = 0xC1, - CEC_OPCODE_REPORT_ARC_ENDED = 0xC2, - CEC_OPCODE_REQUEST_ARC_START = 0xC3, - CEC_OPCODE_REQUEST_ARC_END = 0xC4, - CEC_OPCODE_END_ARC = 0xC5, - CEC_OPCODE_CDC = 0xF8, + CEC_OPCODE_REPORT_SHORT_AUDIO_DESCRIPTORS = 0xA3, + CEC_OPCODE_REQUEST_SHORT_AUDIO_DESCRIPTORS = 0xA4, + CEC_OPCODE_START_ARC = 0xC0, + CEC_OPCODE_REPORT_ARC_STARTED = 0xC1, + CEC_OPCODE_REPORT_ARC_ENDED = 0xC2, + CEC_OPCODE_REQUEST_ARC_START = 0xC3, + CEC_OPCODE_REQUEST_ARC_END = 0xC4, + CEC_OPCODE_END_ARC = 0xC5, + CEC_OPCODE_CDC = 0xF8, /* when this opcode is set, no opcode will be sent to the device. this is one of the reserved numbers */ CEC_OPCODE_NONE = 0xFD } cec_opcode; From 416e1369417ba05a6a64e7ff01def0ac40234d8b Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Sun, 29 Mar 2020 21:31:16 +0200 Subject: [PATCH 68/93] fixed: 5e5788de12ba4f69ee3afad19e6c0e4b45f0ddf4 didn't probe new devices --- src/libcec/CECClient.cpp | 2 +- src/libcec/devices/CECBusDevice.cpp | 13 +++++++++---- src/libcec/devices/CECBusDevice.h | 2 +- src/libcec/devices/CECDeviceMap.cpp | 4 ++-- src/libcec/devices/CECDeviceMap.h | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/libcec/CECClient.cpp b/src/libcec/CECClient.cpp index a8efd99d..3a58cf03 100644 --- a/src/libcec/CECClient.cpp +++ b/src/libcec/CECClient.cpp @@ -1457,7 +1457,7 @@ bool CCECClient::IsActiveDevice(const cec_logical_address iAddress) bool CCECClient::IsActiveDeviceType(const cec_device_type type) { - return m_processor->GetDevices()->IsActiveType(type); + return m_processor->GetDevices()->IsActiveType(type, false); } cec_logical_address CCECClient::GetActiveSource(void) diff --git a/src/libcec/devices/CECBusDevice.cpp b/src/libcec/devices/CECBusDevice.cpp index a0bd183a..32406540 100644 --- a/src/libcec/devices/CECBusDevice.cpp +++ b/src/libcec/devices/CECBusDevice.cpp @@ -301,11 +301,16 @@ bool CCECBusDevice::IsHandledByLibCEC(void) return m_deviceStatus == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC; } -bool CCECBusDevice::IsActive(void) +bool CCECBusDevice::IsActive(bool suppressPoll /* = true */) { - CLockObject lock(m_mutex); - return (m_deviceStatus == CEC_DEVICE_STATUS_PRESENT) || - (m_deviceStatus == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC); + switch (GetStatus(false, suppressPoll)) + { + case CEC_DEVICE_STATUS_PRESENT: + case CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC: + return true; + default: + return false; + } } void CCECBusDevice::SetUnsupportedFeature(cec_opcode opcode) diff --git a/src/libcec/devices/CECBusDevice.h b/src/libcec/devices/CECBusDevice.h index 89e51861..19a43cc3 100644 --- a/src/libcec/devices/CECBusDevice.h +++ b/src/libcec/devices/CECBusDevice.h @@ -111,7 +111,7 @@ namespace CEC virtual const char* GetLogicalAddressName(void) const; virtual bool IsPresent(void); virtual bool IsHandledByLibCEC(void); - virtual bool IsActive(void); + virtual bool IsActive(bool suppressPoll = true); virtual bool HandleCommand(const cec_command &command); virtual bool IsUnsupportedFeature(cec_opcode opcode); diff --git a/src/libcec/devices/CECDeviceMap.cpp b/src/libcec/devices/CECDeviceMap.cpp index 884a61dc..1aa7c07e 100644 --- a/src/libcec/devices/CECDeviceMap.cpp +++ b/src/libcec/devices/CECDeviceMap.cpp @@ -184,14 +184,14 @@ void CCECDeviceMap::GetActive(CECDEVICEVEC &devices) const } } -bool CCECDeviceMap::IsActiveType(const cec_device_type type) const +bool CCECDeviceMap::IsActiveType(const cec_device_type type, bool suppressPoll /* = true */) const { for (auto it = m_busDevices.begin(); it != m_busDevices.end(); ++it) { auto dev = it->second; if (!!dev && (dev->GetType() == type) && - (dev->IsActive())) { + (dev->IsActive(suppressPoll))) { return true; } } diff --git a/src/libcec/devices/CECDeviceMap.h b/src/libcec/devices/CECDeviceMap.h index ca2f029a..7c3e8f71 100644 --- a/src/libcec/devices/CECDeviceMap.h +++ b/src/libcec/devices/CECDeviceMap.h @@ -63,7 +63,7 @@ namespace CEC void GetLibCECControlled(CECDEVICEVEC &devices) const; void GetByLogicalAddresses(CECDEVICEVEC &devices, const cec_logical_addresses &addresses); void GetActive(CECDEVICEVEC &devices) const; - bool IsActiveType(const cec_device_type type) const; + bool IsActiveType(const cec_device_type type, bool suppressPoll = true) const; void GetByType(const cec_device_type type, CECDEVICEVEC &devices) const; void GetChildrenOf(CECDEVICEVEC& devices, CCECBusDevice* device) const; void SignalAll(cec_opcode opcode); From c299d71b68b9476d49573459d0371ebdaf96256d Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Mon, 30 Mar 2020 01:07:30 +0200 Subject: [PATCH 69/93] changed: use hex format for cec-client address input. closes #480 --- src/cec-client/cec-client.cpp | 116 ++++++++++++++++------------------ 1 file changed, 53 insertions(+), 63 deletions(-) diff --git a/src/cec-client/cec-client.cpp b/src/cec-client/cec-client.cpp index 7fdaebc7..e5e0ad90 100644 --- a/src/cec-client/cec-client.cpp +++ b/src/cec-client/cec-client.cpp @@ -135,7 +135,7 @@ inline bool HexStrToInt(const std::string& data, uint8_t& value) //get the first word (separated by whitespace) from string data and place that in word //then remove that word from string data -bool GetWord(std::string& data, std::string& word) +static bool GetWord(std::string& data, std::string& word) { std::stringstream datastream(data); std::string end; @@ -167,6 +167,18 @@ bool GetWord(std::string& data, std::string& word) return true; } +static cec_logical_address GetAddressFromInput(std::string& arguments) +{ + std::string strDev; + if (GetWord(arguments, strDev)) + { + unsigned long iDev = strtoul(strDev.c_str(), NULL, 16); + if ((iDev >= CECDEVICE_TV) && (iDev <= CECDEVICE_BROADCAST)) + return (cec_logical_address)iDev; + } + return CECDEVICE_UNKNOWN; +} + void CecLogMessage(void *UNUSED(cbParam), const cec_log_message* message) { if ((message->level & g_cecLogLevel) == message->level) @@ -396,13 +408,10 @@ bool ProcessCommandSPL(ICECAdapter *parser, const std::string &command, std::str { if (command == "spl") { - std::string strAddress; - cec_logical_address iAddress; - if (GetWord(arguments, strAddress)) + cec_logical_address addr = GetAddressFromInput(arguments); + if ((addr != CECDEVICE_UNKNOWN) && (addr != CECDEVICE_BROADCAST)) { - iAddress = (cec_logical_address)atoi(strAddress.c_str()); - if (iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST) - parser->SetStreamPath(iAddress); + parser->SetStreamPath(addr); return true; } } @@ -495,10 +504,10 @@ bool ProcessCommandLA(ICECAdapter *parser, const std::string &command, std::stri { if (command == "la") { - std::string strvalue; - if (GetWord(arguments, strvalue)) + cec_logical_address addr = GetAddressFromInput(arguments); + if ((addr != CECDEVICE_UNKNOWN) && (addr != CECDEVICE_BROADCAST)) { - parser->SetLogicalAddress((cec_logical_address) atoi(strvalue.c_str())); + parser->SetLogicalAddress(addr); return true; } } @@ -510,10 +519,12 @@ bool ProcessCommandP(ICECAdapter *parser, const std::string &command, std::strin { if (command == "p") { - std::string strPort, strDevice; - if (GetWord(arguments, strDevice) && GetWord(arguments, strPort)) + std::string strPort; + cec_logical_address addr = GetAddressFromInput(arguments); + if ((addr != CECDEVICE_UNKNOWN) && (addr != CECDEVICE_BROADCAST) && + GetWord(arguments, strPort)) { - parser->SetHDMIPort((cec_logical_address)atoi(strDevice.c_str()), (uint8_t)atoi(strPort.c_str())); + parser->SetHDMIPort(addr, (uint8_t)atoi(strPort.c_str())); return true; } } @@ -675,17 +686,13 @@ bool ProcessCommandLANG(ICECAdapter *parser, const std::string &command, std::st { if (command == "lang") { - std::string strDev; - if (GetWord(arguments, strDev)) + cec_logical_address addr = GetAddressFromInput(arguments); + if ((addr != CECDEVICE_UNKNOWN) && (addr != CECDEVICE_BROADCAST)) { - int iDev = atoi(strDev.c_str()); - if (iDev >= 0 && iDev < 15) - { - std::string strLog; - strLog = StringUtils::Format("menu language '%s'", parser->GetDeviceMenuLanguage((cec_logical_address)iDev).c_str()); - PrintToStdOut(strLog.c_str()); - return true; - } + std::string strLog; + strLog = StringUtils::Format("menu language '%s'", parser->GetDeviceMenuLanguage(addr).c_str()); + PrintToStdOut(strLog.c_str()); + return true; } } @@ -696,16 +703,12 @@ bool ProcessCommandVEN(ICECAdapter *parser, const std::string &command, std::str { if (command == "ven") { - std::string strDev; - if (GetWord(arguments, strDev)) + cec_logical_address addr = GetAddressFromInput(arguments); + if ((addr != CECDEVICE_UNKNOWN) && (addr != CECDEVICE_BROADCAST)) { - int iDev = atoi(strDev.c_str()); - if (iDev >= 0 && iDev < 15) - { - uint64_t iVendor = parser->GetDeviceVendorId((cec_logical_address) iDev); - PrintToStdOut("vendor id: %06llx", iVendor); - return true; - } + uint64_t iVendor = parser->GetDeviceVendorId(addr); + PrintToStdOut("vendor id: %06llx", iVendor); + return true; } } @@ -716,16 +719,12 @@ bool ProcessCommandVER(ICECAdapter *parser, const std::string &command, std::str { if (command == "ver") { - std::string strDev; - if (GetWord(arguments, strDev)) + cec_logical_address addr = GetAddressFromInput(arguments); + if ((addr != CECDEVICE_UNKNOWN) && (addr != CECDEVICE_BROADCAST)) { - int iDev = atoi(strDev.c_str()); - if (iDev >= 0 && iDev < 15) - { - cec_version iVersion = parser->GetDeviceCecVersion((cec_logical_address) iDev); - PrintToStdOut("CEC version %s", parser->ToString(iVersion)); - return true; - } + cec_version iVersion = parser->GetDeviceCecVersion(addr); + PrintToStdOut("CEC version %s", parser->ToString(iVersion)); + return true; } } @@ -736,16 +735,12 @@ bool ProcessCommandPOW(ICECAdapter *parser, const std::string &command, std::str { if (command == "pow") { - std::string strDev; - if (GetWord(arguments, strDev)) + cec_logical_address addr = GetAddressFromInput(arguments); + if ((addr != CECDEVICE_UNKNOWN) && (addr != CECDEVICE_BROADCAST)) { - int iDev = atoi(strDev.c_str()); - if (iDev >= 0 && iDev < 15) - { - cec_power_status iPower = parser->GetDevicePowerStatus((cec_logical_address) iDev); - PrintToStdOut("power status: %s", parser->ToString(iPower)); - return true; - } + cec_power_status iPower = parser->GetDevicePowerStatus(addr); + PrintToStdOut("power status: %s", parser->ToString(iPower)); + return true; } } @@ -756,15 +751,11 @@ bool ProcessCommandNAME(ICECAdapter *parser, const std::string &command, std::st { if (command == "name") { - std::string strDev; - if (GetWord(arguments, strDev)) + cec_logical_address addr = GetAddressFromInput(arguments); + if ((addr != CECDEVICE_UNKNOWN) && (addr != CECDEVICE_BROADCAST)) { - int iDev = atoi(strDev.c_str()); - if (iDev >= 0 && iDev < 15) - { - std::string name = parser->GetDeviceOSDName((cec_logical_address)iDev); - PrintToStdOut("OSD name of device %d is '%s'", iDev, name.c_str()); - } + std::string name = parser->GetDeviceOSDName(addr); + PrintToStdOut("OSD name of device %d is '%s'", addr, name.c_str()); return true; } } @@ -793,12 +784,11 @@ bool ProcessCommandAD(ICECAdapter *parser, const std::string &command, std::stri { if (command == "ad") { - std::string strDev; - if (GetWord(arguments, strDev)) + cec_logical_address addr = GetAddressFromInput(arguments); + if ((addr != CECDEVICE_UNKNOWN) && (addr != CECDEVICE_BROADCAST)) { - int iDev = atoi(strDev.c_str()); - if (iDev >= 0 && iDev < 15) - PrintToStdOut("logical address %X is %s", iDev, (parser->IsActiveDevice((cec_logical_address)iDev) ? "active" : "not active")); + PrintToStdOut("logical address %X is %s", addr, + (parser->IsActiveDevice(addr) ? "active" : "not active")); } } From d9c415c1fe09170b56d2f38e42205d1e6a0ac786 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Tue, 31 Mar 2020 11:24:00 +0200 Subject: [PATCH 70/93] fixed: 5e5788de12ba4f69ee3afad19e6c0e4b45f0ddf4 didn't probe when scanning --- src/libcec/devices/CECDeviceMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/devices/CECDeviceMap.cpp b/src/libcec/devices/CECDeviceMap.cpp index 1aa7c07e..4d599ce8 100644 --- a/src/libcec/devices/CECDeviceMap.cpp +++ b/src/libcec/devices/CECDeviceMap.cpp @@ -178,7 +178,7 @@ void CCECDeviceMap::GetActive(CECDEVICEVEC &devices) const for (auto it = m_busDevices.begin(); it != m_busDevices.end(); ++it) { auto dev = it->second; - if (!!dev && dev->IsActive()) { + if (!!dev && dev->IsActive(false)) { devices.push_back(it->second); } } From cb3cc570855ec3dd32b43a3b1405da0e550abf62 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Tue, 31 Mar 2020 11:24:34 +0200 Subject: [PATCH 71/93] added: GetStats() command returns tx/rx stats for devices that support this --- include/cec.h | 2 + include/cectypes.h | 9 +++++ src/cec-client/cec-client.cpp | 27 ++++++++++++- src/libcec/CECClient.cpp | 8 ++++ src/libcec/CECClient.h | 1 + src/libcec/CECProcessor.cpp | 7 ++++ src/libcec/CECProcessor.h | 1 + src/libcec/LibCEC.cpp | 7 ++++ src/libcec/LibCEC.h | 1 + .../adapter/AOCEC/AOCECAdapterCommunication.h | 1 + src/libcec/adapter/AdapterCommunication.h | 2 + .../Exynos/ExynosCECAdapterCommunication.h | 1 + .../USBCECAdapterCommunication.cpp | 39 ++++++++++++++++++- .../Pulse-Eight/USBCECAdapterCommunication.h | 9 +++++ .../Pulse-Eight/USBCECAdapterMessageQueue.cpp | 28 ++++++++++++- .../adapter/RPi/RPiCECAdapterCommunication.h | 1 + .../TDA995x/TDA995xCECAdapterCommunication.h | 1 + 17 files changed, 141 insertions(+), 4 deletions(-) diff --git a/include/cec.h b/include/cec.h index 71450ae0..3fbeb1c1 100644 --- a/include/cec.h +++ b/include/cec.h @@ -453,6 +453,8 @@ namespace CEC * @return True if the command was sent, false otherwise */ virtual bool AudioEnable(bool enable) = 0; + + virtual bool GetStats(struct cec_adapter_stats* stats) = 0; }; }; diff --git a/include/cectypes.h b/include/cectypes.h index 4c7be0d0..956daa3c 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -1357,6 +1357,15 @@ typedef struct libcec_parameter void* paramData; /**< the value of this parameter */ } libcec_parameter; +struct cec_adapter_stats +{ + unsigned int tx_ack; + unsigned int tx_nack; + unsigned int tx_error; + unsigned int rx_total; + unsigned int rx_error; +}; + typedef struct libcec_configuration libcec_configuration; typedef struct ICECCallbacks diff --git a/src/cec-client/cec-client.cpp b/src/cec-client/cec-client.cpp index e5e0ad90..e988f6da 100644 --- a/src/cec-client/cec-client.cpp +++ b/src/cec-client/cec-client.cpp @@ -921,6 +921,30 @@ bool ProcessCommandSCAN(ICECAdapter *parser, const std::string &command, std::st return false; } +bool ProcessCommandSTATS(ICECAdapter *parser, const std::string &command, std::string & UNUSED(arguments)) +{ + if (command == "stats") + { + cec_adapter_stats stats; + if (parser->GetStats(&stats)) + { + std::string strLog; + strLog += StringUtils::Format("tx acked: %u\n", stats.tx_ack); + strLog += StringUtils::Format("tx nacked: %u\n", stats.tx_nack); + strLog += StringUtils::Format("tx error: %u\n", stats.tx_error); + strLog += StringUtils::Format("rx total: %u\n", stats.rx_total); + strLog += StringUtils::Format("rx error: %u\n", stats.rx_error); + PrintToStdOut(strLog.c_str()); + } + else + { + PrintToStdOut("not supported\n"); + } + return true; + } + return false; +} + bool ProcessConsoleCommand(ICECAdapter *parser, std::string &input) { if (!input.empty()) @@ -961,7 +985,8 @@ bool ProcessConsoleCommand(ICECAdapter *parser, std::string &input) ProcessCommandSCAN(parser, command, input) || ProcessCommandSP(parser, command, input) || ProcessCommandSPL(parser, command, input) || - ProcessCommandSELF(parser, command, input); + ProcessCommandSELF(parser, command, input) || + ProcessCommandSTATS(parser, command, input); } } return true; diff --git a/src/libcec/CECClient.cpp b/src/libcec/CECClient.cpp index 3a58cf03..b9002297 100644 --- a/src/libcec/CECClient.cpp +++ b/src/libcec/CECClient.cpp @@ -1704,3 +1704,11 @@ bool CCECClient::AudioEnable(bool enable) audio->EnableAudio(device) : false; } + +bool CCECClient::GetStats(struct cec_adapter_stats* stats) +{ + return !!m_processor ? + m_processor->GetStats(stats) : + false; +} + diff --git a/src/libcec/CECClient.h b/src/libcec/CECClient.h index a1e8b530..ff71e848 100644 --- a/src/libcec/CECClient.h +++ b/src/libcec/CECClient.h @@ -298,6 +298,7 @@ namespace CEC virtual void RescanActiveDevices(void); virtual bool IsLibCECActiveSource(void); bool AudioEnable(bool enable); + bool GetStats(struct cec_adapter_stats* stats); // configuration virtual bool GetCurrentConfiguration(libcec_configuration &configuration); diff --git a/src/libcec/CECProcessor.cpp b/src/libcec/CECProcessor.cpp index c67415cf..fc909d83 100644 --- a/src/libcec/CECProcessor.cpp +++ b/src/libcec/CECProcessor.cpp @@ -333,6 +333,13 @@ bool CCECProcessor::ActivateSource(uint16_t iStreamPath) return bReturn; } +bool CCECProcessor::GetStats(struct cec_adapter_stats* stats) +{ + return !!m_communication ? + m_communication->GetStats(stats) : + false; +} + void CCECProcessor::SetActiveSource(bool bSetTo, bool bClientUnregistered) { if (m_communication) diff --git a/src/libcec/CECProcessor.h b/src/libcec/CECProcessor.h index d93333f1..8b4f0e0a 100644 --- a/src/libcec/CECProcessor.h +++ b/src/libcec/CECProcessor.h @@ -125,6 +125,7 @@ namespace CEC bool SetDeckInfo(cec_deck_info info, bool bSendUpdate = true); bool ActivateSource(uint16_t iStreamPath); void SetActiveSource(bool bSetTo, bool bClientUnregistered); + bool GetStats(struct cec_adapter_stats* stats); bool PollDevice(cec_logical_address iAddress); void SetStandardLineTimeout(uint8_t iTimeout); uint8_t GetStandardLineTimeout(void); diff --git a/src/libcec/LibCEC.cpp b/src/libcec/LibCEC.cpp index d241cb65..70ea733f 100644 --- a/src/libcec/LibCEC.cpp +++ b/src/libcec/LibCEC.cpp @@ -624,3 +624,10 @@ bool CLibCEC::AudioEnable(bool enable) m_client->AudioEnable(enable) : false; } + +bool CLibCEC::GetStats(struct cec_adapter_stats* stats) +{ + return !!m_client ? + m_client->GetStats(stats) : + false; +} diff --git a/src/libcec/LibCEC.h b/src/libcec/LibCEC.h index 46f4a316..1fe3ced5 100644 --- a/src/libcec/LibCEC.h +++ b/src/libcec/LibCEC.h @@ -153,6 +153,7 @@ namespace CEC cec_command CommandFromString(const char* strCommand); bool AudioEnable(bool enable); + bool GetStats(struct cec_adapter_stats* stats); CCECProcessor * m_cec; diff --git a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h index dc494264..2c52c1c5 100644 --- a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h +++ b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h @@ -84,6 +84,7 @@ namespace CEC uint16_t GetAdapterProductId(void) const { return 1; } void HandleLogicalAddressLost(cec_logical_address oldAddress); void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} + bool GetStats(struct cec_adapter_stats* UNUSED(stats)) { return false; } ///} /** @name P8PLATFORM::CThread implementation */ diff --git a/src/libcec/adapter/AdapterCommunication.h b/src/libcec/adapter/AdapterCommunication.h index cac2bc97..88d12f8b 100644 --- a/src/libcec/adapter/AdapterCommunication.h +++ b/src/libcec/adapter/AdapterCommunication.h @@ -238,6 +238,8 @@ namespace CEC */ virtual void SetActiveSource(bool bSetTo, bool bClientUnregistered) = 0; + virtual bool GetStats(struct cec_adapter_stats* stats) = 0; + IAdapterCommunicationCallback *m_callback; }; }; diff --git a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h index 6668aa65..b364ea79 100644 --- a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h +++ b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h @@ -82,6 +82,7 @@ namespace CEC uint16_t GetAdapterProductId(void) const { return 1; } void HandleLogicalAddressLost(cec_logical_address oldAddress); void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} + bool GetStats(struct cec_adapter_stats* UNUSED(stats)) { return false; } ///} /** @name P8PLATFORM::CThread implementation */ diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp index 323ac13b..2ec8f094 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp @@ -76,6 +76,7 @@ CUSBCECAdapterCommunication::CUSBCECAdapterCommunication(IAdapterCommunicationCa m_commands(NULL), m_adapterMessageQueue(NULL) { + memset(&m_stats, 0, sizeof(struct cec_adapter_stats)); m_logicalAddresses.Clear(); for (unsigned int iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++) m_bWaitingForAck[iPtr] = false; @@ -269,7 +270,6 @@ cec_adapter_message_state CUSBCECAdapterCommunication::Write(const cec_command & void *CUSBCECAdapterCommunication::Process(void) { - CCECAdapterMessage msg; LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started"); while (!IsStopped()) @@ -635,6 +635,13 @@ void CUSBCECAdapterCommunication::SetActiveSource(bool bSetTo, bool bClientUnreg m_commands->SetActiveSource(bSetTo, bClientUnregistered); } +bool CUSBCECAdapterCommunication::GetStats(struct cec_adapter_stats* stats) +{ + CLockObject lock(m_statsMutex); + memcpy(stats, &m_stats, sizeof(struct cec_adapter_stats)); + return true; +} + bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void) { return GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE && @@ -717,6 +724,36 @@ uint16_t CUSBCECAdapterCommunication::GetPhysicalAddress(void) return iPA; } +void CUSBCECAdapterCommunication::OnRxSuccess(void) +{ + CLockObject lock(m_statsMutex); + ++m_stats.rx_total; +} + +void CUSBCECAdapterCommunication::OnRxError(void) +{ + CLockObject lock(m_statsMutex); + ++m_stats.rx_error; +} + +void CUSBCECAdapterCommunication::OnTxAck(void) +{ + CLockObject lock(m_statsMutex); + ++m_stats.tx_ack; +} + +void CUSBCECAdapterCommunication::OnTxNack(void) +{ + CLockObject lock(m_statsMutex); + ++m_stats.tx_nack; +} + +void CUSBCECAdapterCommunication::OnTxError(void) +{ + CLockObject lock(m_statsMutex); + ++m_stats.tx_error; +} + void *CAdapterPingThread::Process(void) { while (!IsStopped()) diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h index c7978ca9..b9d2ada5 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h @@ -94,10 +94,17 @@ namespace CEC uint16_t GetAdapterVendorId(void) const; uint16_t GetAdapterProductId(void) const; void SetActiveSource(bool bSetTo, bool bClientUnregistered); + bool GetStats(struct cec_adapter_stats* stats); ///} bool ProvidesExtendedResponse(void); + void OnRxSuccess(void); + void OnRxError(void); + void OnTxAck(void); + void OnTxNack(void); + void OnTxError(void); + void *Process(void); private: @@ -185,6 +192,8 @@ namespace CEC CUSBCECAdapterCommands * m_commands; /**< commands that can be sent to the adapter */ CCECAdapterMessageQueue * m_adapterMessageQueue; /**< the incoming and outgoing message queue */ cec_logical_addresses m_logicalAddresses; /**< the logical address list that this instance is using */ + struct cec_adapter_stats m_stats; + P8PLATFORM::CMutex m_statsMutex; P8PLATFORM::CMutex m_waitingMutex; }; diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp index e4540355..87b2d985 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp @@ -238,6 +238,7 @@ bool CCECAdapterMessageQueueEntry::MessageReceivedTransmitSucceeded(const CCECAd #endif m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED; m_message->response = message.packet; + m_queue->m_com->OnTxAck(); } else { @@ -261,13 +262,30 @@ bool CCECAdapterMessageQueueEntry::MessageReceivedResponse(const CCECAdapterMess m_queue->m_com->m_callback->GetLib()->AddLog(CEC_LOG_DEBUG, "%s - received response - %s", ToString(), message.ToString().c_str()); #else if (message.IsError()) + { m_queue->m_com->m_callback->GetLib()->AddLog(CEC_LOG_DEBUG, "%s - received response - %s", ToString(), message.ToString().c_str()); + if (m_message->IsTransmission() && (message.Message() != MSGCODE_TRANSMIT_FAILED_ACK)) + m_queue->m_com->OnTxError(); + } #endif m_message->response = message.packet; if (m_message->IsTransmission()) - m_message->state = message.Message() == MSGCODE_TRANSMIT_SUCCEEDED ? ADAPTER_MESSAGE_STATE_SENT_ACKED : ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED; + { + if (message.Message() == MSGCODE_TRANSMIT_SUCCEEDED) + { + m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED; + m_queue->m_com->OnTxAck(); + } + else + { + m_message->state = ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED; + m_queue->m_com->OnTxNack(); + } + } else + { m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED; + } } Signal(); @@ -365,7 +383,7 @@ void CCECAdapterMessageQueue::MessageReceived(const CCECAdapterMessage &msg) bool bHandled(false); CLockObject lock(m_mutex); /* send the received message to each entry in the queue until it is handled */ - for (std::map::iterator it = m_messages.begin(); !bHandled && it != m_messages.end(); it++) + for (auto it = m_messages.begin(); !bHandled && it != m_messages.end(); ++it) bHandled = it->second->MessageReceived(msg); if (!bHandled) @@ -376,7 +394,10 @@ void CCECAdapterMessageQueue::MessageReceived(const CCECAdapterMessage &msg) m_com->m_callback->GetLib()->AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString().c_str()); #else if (bIsError) + { + m_com->OnRxError(); m_com->m_callback->GetLib()->AddLog(CEC_LOG_WARNING, msg.ToString().c_str()); + } #endif /* push this message to the current frame */ @@ -384,7 +405,10 @@ void CCECAdapterMessageQueue::MessageReceived(const CCECAdapterMessage &msg) { /* and push the current frame back over the callback method when a full command was received */ if (m_com->IsInitialised()) + { + m_com->OnRxSuccess(); m_com->m_callback->OnCommandReceived(m_currentCECFrame); + } /* clear the current frame */ m_currentCECFrame.Clear(); diff --git a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h index 5e3318c3..90027e30 100644 --- a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h +++ b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h @@ -88,6 +88,7 @@ namespace CEC uint16_t GetAdapterVendorId(void) const { return RPI_ADAPTER_VID; } uint16_t GetAdapterProductId(void) const { return RPI_ADAPTER_PID; } void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} + bool GetStats(struct cec_adapter_stats* UNUSED(stats)) { return false; } ///} bool IsInitialised(void); diff --git a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h index 40e46013..0cb8e23d 100644 --- a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h +++ b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h @@ -93,6 +93,7 @@ namespace CEC uint16_t GetAdapterProductId(void) const { return TDA995X_ADAPTER_PID; } void HandleLogicalAddressLost(cec_logical_address oldAddress); void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} + bool GetStats(struct cec_adapter_stats* UNUSED(stats)) { return false; } ///} /** @name P8PLATFORM::CThread implementation */ From 961d4cb0c861ff27038a94dc7f3047511b419d07 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 1 Apr 2020 00:27:11 +0200 Subject: [PATCH 72/93] added: auto power on support (fw v9) - TODO: version bump --- include/cectypes.h | 2 + src/cec-client/cec-client.cpp | 20 +++++ src/libcec/CECClient.cpp | 3 + .../Pulse-Eight/USBCECAdapterCommands.cpp | 74 +++++++++++++++++-- .../Pulse-Eight/USBCECAdapterCommands.h | 4 + .../Pulse-Eight/USBCECAdapterMessage.cpp | 4 + .../Pulse-Eight/USBCECAdapterMessage.h | 2 + .../Pulse-Eight/USBCECAdapterMessageQueue.cpp | 3 +- 8 files changed, 103 insertions(+), 9 deletions(-) diff --git a/include/cectypes.h b/include/cectypes.h index 956daa3c..02a5af55 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -1480,6 +1480,7 @@ struct libcec_configuration uint32_t iButtonReleaseDelayMs;/*!< duration after last update until a button is considered released */ uint32_t iDoubleTapTimeoutMs; /*!< prevent double taps within this timeout. defaults to 200ms. added in 4.0.0 */ uint8_t bAutoWakeAVR; /*!< set to 1 to automatically waking an AVR when the source is activated. added in 4.0.0 */ + uint8_t bAutoPowerOn; /*!< set to 1 and save eeprom config to wake the tv when usb is powered. added in 5.0.0 / fw v9 */ #ifdef __cplusplus libcec_configuration(void) { Clear(); } @@ -1548,6 +1549,7 @@ struct libcec_configuration iButtonRepeatRateMs = 0; iButtonReleaseDelayMs = CEC_BUTTON_TIMEOUT; bAutoWakeAVR = 0; + bAutoPowerOn = 0; memset(strDeviceName, 0, 13); deviceTypes.Clear(); diff --git a/src/cec-client/cec-client.cpp b/src/cec-client/cec-client.cpp index e988f6da..51b7f177 100644 --- a/src/cec-client/cec-client.cpp +++ b/src/cec-client/cec-client.cpp @@ -1215,6 +1215,26 @@ bool ProcessCommandLineArguments(int argc, char *argv[]) } } #endif + else if (!strcmp(argv[iArgPtr], "-aw") || + !strcmp(argv[iArgPtr], "--autowake")) + { + if (argc >= iArgPtr + 2) + { + bool wake = (*argv[iArgPtr + 1] == '1'); + if (wake) + { + std::cout << "enabling auto-wake" << std::endl; + g_config.bAutoPowerOn = 1; + } + else + { + std::cout << "disabling auto-wake" << std::endl; + g_config.bAutoPowerOn = 0; + } + ++iArgPtr; + } + ++iArgPtr; + } else { g_strPort = argv[iArgPtr++]; diff --git a/src/libcec/CECClient.cpp b/src/libcec/CECClient.cpp index b9002297..e78162df 100644 --- a/src/libcec/CECClient.cpp +++ b/src/libcec/CECClient.cpp @@ -143,6 +143,8 @@ bool CCECClient::OnRegister(void) if (m_configuration.bActivateSource == 1) GetPrimaryDevice()->ActivateSource(500); + PersistConfiguration(m_configuration); + return true; } @@ -912,6 +914,7 @@ bool CCECClient::SetConfiguration(const libcec_configuration &configuration) m_configuration.iButtonRepeatRateMs = configuration.iButtonRepeatRateMs; m_configuration.iButtonReleaseDelayMs = configuration.iButtonReleaseDelayMs; m_configuration.bAutoWakeAVR = configuration.bAutoWakeAVR; + m_configuration.bAutoPowerOn = configuration.bAutoPowerOn; } bool bNeedReinit(false); diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp index 92b33b7a..fdd7a246 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp @@ -129,6 +129,22 @@ bool CUSBCECAdapterCommands::RequestSettingAutoEnabled(void) return false; } +bool CUSBCECAdapterCommands::RequestSettingAutoPowerOn(void) +{ +#ifdef CEC_DEBUGGING + LIB_CEC->AddLog(CEC_LOG_DEBUG, "requesting auto power on setting"); +#endif + + cec_datapacket response = RequestSetting(MSGCODE_GET_AUTO_POWER_ON); + if (response.size == 1) + { + m_settingAutoOn = response[0]; + LIB_CEC->AddLog(CEC_LOG_DEBUG, "using auto on setting: '%u'", m_settingAutoOn ? 1 : 0); + return true; + } + return false; +} + bool CUSBCECAdapterCommands::RequestSettingCECVersion(void) { #ifdef CEC_DEBUGGING @@ -410,6 +426,42 @@ bool CUSBCECAdapterCommands::SetSettingPhysicalAddress(uint16_t iPhysicalAddress return bReturn; } +bool CUSBCECAdapterCommands::SetSettingAutoPowerOn(bool autoOn) +{ + bool bReturn(false); + + if (m_persistedConfiguration.iFirmwareVersion < 9) + // only supported by v9+ + return bReturn; + + /* check whether this value was changed */ + { + CLockObject lock(m_mutex); + if (m_settingAutoOn == autoOn) + return bReturn; + m_bNeedsWrite = true; + } + + CCECAdapterMessage params; + params.PushEscaped(autoOn ? 1 : 0); + CCECAdapterMessage *message = m_comm->SendCommand(MSGCODE_SET_AUTO_POWER_ON, params); + bReturn = message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED; + SAFE_DELETE(message); + + if (bReturn) + { + CLockObject lock(m_mutex); + m_settingAutoOn = autoOn; + LIB_CEC->AddLog(CEC_LOG_WARNING, "auto power on %s", autoOn ? "enabled" : "disabled"); + } + else + { + LIB_CEC->AddLog(CEC_LOG_WARNING, "failed to %s auto power on", autoOn ? "enable" : "disable"); + } + + return bReturn; +} + bool CUSBCECAdapterCommands::SetSettingCECVersion(cec_version version) { bool bReturn(false); @@ -499,8 +551,11 @@ bool CUSBCECAdapterCommands::PersistConfiguration(const libcec_configuration &co bReturn |= SetSettingDefaultLogicalAddress(configuration.logicalAddresses.primary); bReturn |= SetSettingLogicalAddressMask(CLibCEC::GetMaskForType(configuration.logicalAddresses.primary)); bReturn |= SetSettingPhysicalAddress(configuration.iPhysicalAddress); - bReturn |= SetSettingCECVersion(configuration.cecVersion); bReturn |= SetSettingOSDName(configuration.strDeviceName); + if (m_persistedConfiguration.iFirmwareVersion >= 9) + bReturn |= SetSettingAutoPowerOn(configuration.bAutoPowerOn); + else + bReturn |= SetSettingCECVersion(configuration.cecVersion); return bReturn; } @@ -518,13 +573,16 @@ bool CUSBCECAdapterCommands::RequestSettings(void) return true; bool bReturn(true); - bReturn &= RequestSettingAutoEnabled(); - bReturn &= RequestSettingCECVersion(); - bReturn &= RequestSettingDefaultLogicalAddress(); - bReturn &= RequestSettingDeviceType(); - bReturn &= RequestSettingLogicalAddressMask(); - bReturn &= RequestSettingOSDName(); - bReturn &= RequestSettingPhysicalAddress(); + bReturn |= RequestSettingAutoEnabled(); + bReturn |= RequestSettingDefaultLogicalAddress(); + bReturn |= RequestSettingDeviceType(); + bReturn |= RequestSettingLogicalAddressMask(); + bReturn |= RequestSettingOSDName(); + bReturn |= RequestSettingPhysicalAddress(); + if (m_persistedConfiguration.iFirmwareVersion >= 9) + bReturn |= RequestSettingAutoPowerOn(); + else + bReturn |= RequestSettingCECVersion(); // don't read the following settings: // - auto enabled (always enabled) diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h index 24f16537..552c5cf0 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h @@ -227,6 +227,9 @@ namespace CEC */ bool RequestSettingCECVersion(void); + bool SetSettingAutoPowerOn(bool autoOn); + bool RequestSettingAutoPowerOn(void); + /*! * @brief Change the value of the "OSD name" setting, used when the device is in autonomous mode. * @param strOSDName The new value. @@ -244,6 +247,7 @@ namespace CEC bool m_bSettingsRetrieved; /**< true when the settings were read from the eeprom, false otherwise */ bool m_bSettingAutoEnabled; /**< the value of the auto-enabled setting */ cec_version m_settingCecVersion; /**< the value of the cec version setting */ + bool m_settingAutoOn; /**< the value of the auto power on setting */ uint16_t m_iSettingLAMask; /**< the value of the LA mask setting */ bool m_bNeedsWrite; /**< true when we sent changed settings to the adapter that have not been persisted */ libcec_configuration m_persistedConfiguration; /**< the configuration that is persisted in the eeprom */ diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.cpp index b7b891cb..9ddb61d7 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.cpp @@ -231,6 +231,10 @@ const char *CCECAdapterMessage::ToString(cec_adapter_messagecode msgCode) return "WRITE_EEPROM"; case MSGCODE_GET_ADAPTER_TYPE: return "GET_ADAPTER_TYPE"; + case MSGCODE_GET_AUTO_POWER_ON: + return "GET_AUTO_POWER_ON"; + case MSGCODE_SET_AUTO_POWER_ON: + return "SET_AUTO_POWER_ON"; default: break; } diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.h index 2e8123dc..756b4610 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.h @@ -81,6 +81,8 @@ namespace CEC MSGCODE_WRITE_EEPROM, MSGCODE_GET_ADAPTER_TYPE, MSGCODE_SET_ACTIVE_SOURCE, + MSGCODE_GET_AUTO_POWER_ON, + MSGCODE_SET_AUTO_POWER_ON, MSGCODE_FRAME_EOM = 0x80, MSGCODE_FRAME_ACK = 0x40, diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp index 87b2d985..80a36619 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp @@ -164,7 +164,8 @@ bool CCECAdapterMessageQueueEntry::IsResponse(const CCECAdapterMessage &msg) thisMsgCode == MSGCODE_SET_OSD_NAME || thisMsgCode == MSGCODE_WRITE_EEPROM || thisMsgCode == MSGCODE_TRANSMIT_IDLETIME || - thisMsgCode == MSGCODE_SET_ACTIVE_SOURCE) + thisMsgCode == MSGCODE_SET_ACTIVE_SOURCE || + thisMsgCode == MSGCODE_SET_AUTO_POWER_ON) return thisMsgCode == msgResponse; if (!m_message->IsTransmission()) From 89a92a2838d0d164df24e6982ddea7a48eb9664e Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 6 Sep 2017 17:37:05 +0200 Subject: [PATCH 73/93] Add Linux CEC Adapter --- docs/README.linux.md | 8 +- include/cectypes.h | 11 + src/libcec/CECTypeUtils.h | 2 + src/libcec/CMakeLists.txt | 2 + src/libcec/adapter/AdapterFactory.cpp | 26 +- .../Linux/LinuxCECAdapterCommunication.cpp | 438 ++++++++++++++++++ .../Linux/LinuxCECAdapterCommunication.h | 96 ++++ .../Linux/LinuxCECAdapterDetection.cpp | 50 ++ .../adapter/Linux/LinuxCECAdapterDetection.h | 51 ++ src/libcec/cmake/CheckPlatformSupport.cmake | 12 + src/libcec/cmake/DisplayPlatformSupport.cmake | 6 + src/libcec/env.h.in | 3 + 12 files changed, 702 insertions(+), 3 deletions(-) create mode 100644 src/libcec/adapter/Linux/LinuxCECAdapterCommunication.cpp create mode 100644 src/libcec/adapter/Linux/LinuxCECAdapterCommunication.h create mode 100644 src/libcec/adapter/Linux/LinuxCECAdapterDetection.cpp create mode 100644 src/libcec/adapter/Linux/LinuxCECAdapterDetection.h diff --git a/docs/README.linux.md b/docs/README.linux.md index 91928923..39735c18 100644 --- a/docs/README.linux.md +++ b/docs/README.linux.md @@ -51,5 +51,11 @@ Pass the argument `-DHAVE_TDA995X_API=1` to the cmake command in the compilation cmake -DHAVE_TDA995X_API=1 .. ``` +### Linux CEC Framework (v4.10+) +Pass the argument `-DHAVE_LINUX_API=1` to the cmake command in the compilation instructions: +``` +cmake -DHAVE_LINUX_API=1 .. +``` + ### Debian / Ubuntu .deb packaging -See [docs/README.debian.md](README.debian.md). +See [docs/README.debian.md](README.debian.md). \ No newline at end of file diff --git a/include/cectypes.h b/include/cectypes.h index 02a5af55..024cc23c 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -281,6 +281,16 @@ namespace CEC { */ #define CEC_MAX_DATA_PACKET_SIZE (16 * 4) +/*! + * the path to use for the Linux CEC device + */ +#define CEC_LINUX_PATH "/dev/cec0" + +/*! + * the name of the virtual COM port to use for the Linux' CEC wire + */ +#define CEC_LINUX_VIRTUAL_COM "Linux" + /*! * the path to use for the AOCEC HDMI CEC device */ @@ -864,6 +874,7 @@ typedef enum cec_adapter_type ADAPTERTYPE_RPI = 0x100, ADAPTERTYPE_TDA995x = 0x200, ADAPTERTYPE_EXYNOS = 0x300, + ADAPTERTYPE_LINUX = 0x400, ADAPTERTYPE_AOCEC = 0x500 } cec_adapter_type; diff --git a/src/libcec/CECTypeUtils.h b/src/libcec/CECTypeUtils.h index eeb07fbe..80c5b3b4 100644 --- a/src/libcec/CECTypeUtils.h +++ b/src/libcec/CECTypeUtils.h @@ -768,6 +768,8 @@ namespace CEC return "Raspberry Pi"; case ADAPTERTYPE_TDA995x: return "TDA995x"; + case ADAPTERTYPE_LINUX: + return "Linux"; default: return "unknown"; } diff --git a/src/libcec/CMakeLists.txt b/src/libcec/CMakeLists.txt index 6baee69e..74fe5f37 100644 --- a/src/libcec/CMakeLists.txt +++ b/src/libcec/CMakeLists.txt @@ -89,6 +89,8 @@ set(CEC_HEADERS devices/CECRecordingDevice.h adapter/Exynos/ExynosCEC.h adapter/Exynos/ExynosCECAdapterDetection.h adapter/Exynos/ExynosCECAdapterCommunication.h + adapter/Linux/LinuxCECAdapterDetection.h + adapter/Linux/LinuxCECAdapterCommunication.h adapter/AOCEC/AOCEC.h adapter/AOCEC/AOCECAdapterDetection.h adapter/AOCEC/AOCECAdapterCommunication.h diff --git a/src/libcec/adapter/AdapterFactory.cpp b/src/libcec/adapter/AdapterFactory.cpp index 91195ea0..323c2724 100644 --- a/src/libcec/adapter/AdapterFactory.cpp +++ b/src/libcec/adapter/AdapterFactory.cpp @@ -58,6 +58,11 @@ #include "Exynos/ExynosCECAdapterCommunication.h" #endif +#if defined(HAVE_LINUX_API) +#include "Linux/LinuxCECAdapterDetection.h" +#include "Linux/LinuxCECAdapterCommunication.h" +#endif + #if defined(HAVE_AOCEC_API) #include "AOCEC/AOCECAdapterDetection.h" #include "AOCEC/AOCECAdapterCommunication.h" @@ -131,6 +136,18 @@ int8_t CAdapterFactory::DetectAdapters(cec_adapter_descriptor *deviceList, uint8 } #endif +#if defined(HAVE_LINUX_API) + if (iAdaptersFound < iBufSize && CLinuxCECAdapterDetection::FindAdapter()) + { + snprintf(deviceList[iAdaptersFound].strComPath, sizeof(deviceList[iAdaptersFound].strComPath), CEC_LINUX_PATH); + snprintf(deviceList[iAdaptersFound].strComName, sizeof(deviceList[iAdaptersFound].strComName), CEC_LINUX_VIRTUAL_COM); + deviceList[iAdaptersFound].iVendorId = 0; + deviceList[iAdaptersFound].iProductId = 0; + deviceList[iAdaptersFound].adapterType = ADAPTERTYPE_LINUX; + iAdaptersFound++; + } +#endif + #if defined(HAVE_AOCEC_API) if (iAdaptersFound < iBufSize && CAOCECAdapterDetection::FindAdapter()) { @@ -144,7 +161,7 @@ int8_t CAdapterFactory::DetectAdapters(cec_adapter_descriptor *deviceList, uint8 #endif -#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_AOCEC_API) +#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_LINUX_API) && !defined(HAVE_AOCEC_API) #error "libCEC doesn't have support for any type of adapter. please check your build system or configuration" #endif @@ -163,6 +180,11 @@ IAdapterCommunication *CAdapterFactory::GetInstance(const char *strPort, uint16_ return new CExynosCECAdapterCommunication(m_lib->m_cec); #endif +#if defined(HAVE_LINUX_API) + if (!strcmp(strPort, CEC_LINUX_VIRTUAL_COM)) + return new CLinuxCECAdapterCommunication(m_lib->m_cec); +#endif + #if defined(HAVE_AOCEC_API) if (!strcmp(strPort, CEC_AOCEC_VIRTUAL_COM)) return new CAOCECAdapterCommunication(m_lib->m_cec); @@ -177,7 +199,7 @@ IAdapterCommunication *CAdapterFactory::GetInstance(const char *strPort, uint16_ return new CUSBCECAdapterCommunication(m_lib->m_cec, strPort, iBaudRate); #endif -#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_EXYNOS_API) && !defined(HAVE_AOCEC_API) +#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_EXYNOS_API) && !defined(HAVE_LINUX_API) && !defined(HAVE_AOCEC_API) return NULL; #endif } diff --git a/src/libcec/adapter/Linux/LinuxCECAdapterCommunication.cpp b/src/libcec/adapter/Linux/LinuxCECAdapterCommunication.cpp new file mode 100644 index 00000000..6a288352 --- /dev/null +++ b/src/libcec/adapter/Linux/LinuxCECAdapterCommunication.cpp @@ -0,0 +1,438 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC Linux CEC Adapter is Copyright (C) 2017-2019 Jonas Karlman + * based heavily on: + * libCEC AOCEC Code is Copyright (C) 2016 Gerald Dachs + * libCEC Exynos Code is Copyright (C) 2014 Valentin Manea + * libCEC(R) is Copyright (C) 2011-2015 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 + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" +#include +#include + +#if defined(HAVE_LINUX_API) +#include "LinuxCECAdapterCommunication.h" +#include "CECTypeUtils.h" +#include "LibCEC.h" +#include "p8-platform/util/buffer.h" +#include + +using namespace CEC; +using namespace P8PLATFORM; + +#define LIB_CEC m_callback->GetLib() + +// Required capabilities +#define CEC_LINUX_CAPABILITIES (CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH) + +CLinuxCECAdapterCommunication::CLinuxCECAdapterCommunication(IAdapterCommunicationCallback *callback) + : IAdapterCommunication(callback) +{ + m_fd = INVALID_SOCKET_VALUE; +} + +CLinuxCECAdapterCommunication::~CLinuxCECAdapterCommunication(void) +{ + Close(); +} + +bool CLinuxCECAdapterCommunication::Open(uint32_t UNUSED(iTimeoutMs), bool UNUSED(bSkipChecks), bool bStartListening) +{ + if (IsOpen()) + Close(); + + if ((m_fd = open(CEC_LINUX_PATH, O_RDWR)) >= 0) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "CLinuxCECAdapterCommunication::Open - m_fd=%d bStartListening=%d", m_fd, bStartListening); + + // Ensure the CEC device supports required capabilities + struct cec_caps caps = {}; + if (ioctl(m_fd, CEC_ADAP_G_CAPS, &caps) || (caps.capabilities & CEC_LINUX_CAPABILITIES) != CEC_LINUX_CAPABILITIES) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::Open - ioctl CEC_ADAP_G_CAPS failed - capabilities=%02x errno=%d", caps.capabilities, errno); + Close(); + return false; + } + + if (!bStartListening) + { + Close(); + return true; + } + + // This is an exclusive follower, in addition put the CEC device into passthrough mode + uint32_t mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU; + if (ioctl(m_fd, CEC_S_MODE, &mode)) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::Open - ioctl CEC_S_MODE failed - errno=%d", errno); + Close(); + return false; + } + + uint16_t addr; + if (ioctl(m_fd, CEC_ADAP_G_PHYS_ADDR, &addr)) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::Open - ioctl CEC_ADAP_G_PHYS_ADDR failed - errno=%d", errno); + Close(); + return false; + } + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "CLinuxCECAdapterCommunication::Open - ioctl CEC_ADAP_G_PHYS_ADDR - addr=%04x", addr); + + if (addr == CEC_PHYS_ADDR_INVALID) + LIB_CEC->AddLog(CEC_LOG_WARNING, "CLinuxCECAdapterCommunication::Open - physical address is invalid"); + + // Clear existing logical addresses and set the CEC device to the unconfigured state + struct cec_log_addrs log_addrs = {}; + if (ioctl(m_fd, CEC_ADAP_S_LOG_ADDRS, &log_addrs)) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::Open - ioctl CEC_ADAP_S_LOG_ADDRS failed - errno=%d", errno); + Close(); + return false; + } + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "CLinuxCECAdapterCommunication::Open - ioctl CEC_ADAP_S_LOG_ADDRS - log_addr_mask=%04x num_log_addrs=%u", log_addrs.log_addr_mask, log_addrs.num_log_addrs); + + // Set logical address to unregistered, without any logical address configured no messages is transmitted or received + log_addrs = {}; + log_addrs.cec_version = CEC_OP_CEC_VERSION_1_4; + log_addrs.vendor_id = CEC_VENDOR_PULSE_EIGHT; + log_addrs.num_log_addrs = 1; + log_addrs.flags = CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK; + log_addrs.log_addr[0] = CEC_LOG_ADDR_UNREGISTERED; + log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_SWITCH; + log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; + log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH; + if (ioctl(m_fd, CEC_ADAP_S_LOG_ADDRS, &log_addrs)) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::Open - ioctl CEC_ADAP_S_LOG_ADDRS failed - errno=%d", errno); + Close(); + return false; + } + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "CLinuxCECAdapterCommunication::Open - ioctl CEC_ADAP_S_LOG_ADDRS - log_addr_mask=%04x num_log_addrs=%u", log_addrs.log_addr_mask, log_addrs.num_log_addrs); + + if (CreateThread()) + return true; + + Close(); + } + + return false; +} + +void CLinuxCECAdapterCommunication::Close(void) +{ + StopThread(0); + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "CLinuxCECAdapterCommunication::Close - m_fd=%d", m_fd); + + close(m_fd); + m_fd = INVALID_SOCKET_VALUE; +} + +bool CLinuxCECAdapterCommunication::IsOpen(void) +{ + return m_fd != INVALID_SOCKET_VALUE; +} + +cec_adapter_message_state CLinuxCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t UNUSED(iLineTimeout), bool UNUSED(bIsReply)) +{ + if (IsOpen()) + { + struct cec_msg msg; + cec_msg_init(&msg, data.initiator, data.destination); + + if (data.opcode_set) + { + msg.msg[msg.len++] = data.opcode; + + if (data.parameters.size) + { + memcpy(&msg.msg[msg.len], data.parameters.data, data.parameters.size); + msg.len += data.parameters.size; + } + } + + if (ioctl(m_fd, CEC_TRANSMIT, &msg)) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::Write - ioctl CEC_TRANSMIT failed - tx_status=%02x errno=%d", msg.tx_status, errno); + return ADAPTER_MESSAGE_STATE_ERROR; + } + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "CLinuxCECAdapterCommunication::Write - ioctl CEC_TRANSMIT - tx_status=%02x len=%d addr=%02x opcode=%02x", msg.tx_status, msg.len, msg.msg[0], cec_msg_opcode(&msg)); + + // The CEC driver will make re-transmission attempts + bRetry = false; + + if (msg.tx_status & CEC_TX_STATUS_OK) + return ADAPTER_MESSAGE_STATE_SENT_ACKED; + + if (msg.tx_status & CEC_TX_STATUS_NACK) + return ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED; + + return ADAPTER_MESSAGE_STATE_ERROR; + } + + return ADAPTER_MESSAGE_STATE_UNKNOWN; +} + +bool CLinuxCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses) +{ + if (IsOpen()) + { + struct cec_log_addrs log_addrs = {}; + if (ioctl(m_fd, CEC_ADAP_G_LOG_ADDRS, &log_addrs)) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::SetLogicalAddresses - ioctl CEC_ADAP_G_LOG_ADDRS failed - errno=%d", errno); + return false; + } + + // TODO: Claiming a logical address will only work when CEC device has a valid physical address + + // Clear existing logical addresses and set the CEC device to the unconfigured state + if (log_addrs.num_log_addrs) + { + log_addrs = {}; + if (ioctl(m_fd, CEC_ADAP_S_LOG_ADDRS, &log_addrs)) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::SetLogicalAddresses - ioctl CEC_ADAP_S_LOG_ADDRS failed - errno=%d", errno); + return false; + } + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "CLinuxCECAdapterCommunication::SetLogicalAddresses - ioctl CEC_ADAP_S_LOG_ADDRS - log_addr_mask=%04x num_log_addrs=%u", log_addrs.log_addr_mask, log_addrs.num_log_addrs); + } + + if (!addresses.IsEmpty()) + { + // NOTE: This can only be configured when num_log_addrs > 0 + // and gets reset when num_log_addrs = 0 + log_addrs.cec_version = CEC_OP_CEC_VERSION_1_4; + log_addrs.vendor_id = CEC_VENDOR_PULSE_EIGHT; + + // TODO: Support more then the primary logical address + log_addrs.num_log_addrs = 1; + log_addrs.log_addr[0] = addresses.primary; + + switch (addresses.primary) + { + case CECDEVICE_AUDIOSYSTEM: + log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM; + log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM; + log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM; + break; + case CECDEVICE_PLAYBACKDEVICE1: + case CECDEVICE_PLAYBACKDEVICE2: + case CECDEVICE_PLAYBACKDEVICE3: + log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_PLAYBACK; + log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; + log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_PLAYBACK; + break; + case CECDEVICE_RECORDINGDEVICE1: + case CECDEVICE_RECORDINGDEVICE2: + case CECDEVICE_RECORDINGDEVICE3: + log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_RECORD; + log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_RECORD; + log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_RECORD; + break; + case CECDEVICE_TUNER1: + case CECDEVICE_TUNER2: + case CECDEVICE_TUNER3: + case CECDEVICE_TUNER4: + log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_TUNER; + log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_TUNER; + log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_TUNER; + break; + case CECDEVICE_TV: + log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_TV; + log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV; + log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_TV; + break; + default: + log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_SWITCH; + log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; + log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH; + break; + } + } + else + log_addrs.num_log_addrs = 0; + + if (ioctl(m_fd, CEC_ADAP_S_LOG_ADDRS, &log_addrs)) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::SetLogicalAddresses - ioctl CEC_ADAP_S_LOG_ADDRS failed - errno=%d", errno); + return false; + } + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "CLinuxCECAdapterCommunication::SetLogicalAddresses - ioctl CEC_ADAP_S_LOG_ADDRS - log_addr_mask=%04x num_log_addrs=%u", log_addrs.log_addr_mask, log_addrs.num_log_addrs); + + if (log_addrs.num_log_addrs && !log_addrs.log_addr_mask) + return false; + + return true; + } + + return false; +} + +cec_logical_addresses CLinuxCECAdapterCommunication::GetLogicalAddresses(void) const +{ + cec_logical_addresses addresses; + addresses.Clear(); + + if (m_fd != INVALID_SOCKET_VALUE) + { + struct cec_log_addrs log_addrs = {}; + if (ioctl(m_fd, CEC_ADAP_G_LOG_ADDRS, &log_addrs)) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::GetLogicalAddresses - ioctl CEC_ADAP_G_LOG_ADDRS failed - errno=%d", errno); + return addresses; + } + + for (int i = 0; i < log_addrs.num_log_addrs; i++) + addresses.Set(cec_logical_address(log_addrs.log_addr[i])); + } + + return addresses; +} + +uint16_t CLinuxCECAdapterCommunication::GetPhysicalAddress(void) +{ + if (IsOpen()) + { + uint16_t addr; + if (!ioctl(m_fd, CEC_ADAP_G_PHYS_ADDR, &addr)) + return addr; + + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::GetPhysicalAddress - ioctl CEC_ADAP_G_PHYS_ADDR failed - errno=%d", errno); + } + + return CEC_INVALID_PHYSICAL_ADDRESS; +} + +cec_vendor_id CLinuxCECAdapterCommunication::GetVendorId(void) +{ + if (IsOpen()) + { + struct cec_log_addrs log_addrs = {}; + if (!ioctl(m_fd, CEC_ADAP_G_LOG_ADDRS, &log_addrs)) + return cec_vendor_id(log_addrs.vendor_id); + + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::GetVendorId - ioctl CEC_ADAP_G_LOG_ADDRS failed - errno=%d", errno); + } + + return CEC_VENDOR_UNKNOWN; +} + +void *CLinuxCECAdapterCommunication::Process(void) +{ + CTimeout phys_addr_timeout; + bool phys_addr_changed = false; + uint16_t phys_addr = CEC_INVALID_PHYSICAL_ADDRESS; + fd_set rd_fds; + fd_set ex_fds; + + while (!IsStopped()) + { + struct timeval timeval = {}; + timeval.tv_sec = 1; + + FD_ZERO(&rd_fds); + FD_ZERO(&ex_fds); + FD_SET(m_fd, &rd_fds); + FD_SET(m_fd, &ex_fds); + + if (select(m_fd + 1, &rd_fds, NULL, &ex_fds, &timeval) < 0) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::Process - select failed - errno=%d", errno); + break; + } + + if (FD_ISSET(m_fd, &ex_fds)) + { + struct cec_event ev = {}; + if (ioctl(m_fd, CEC_DQEVENT, &ev)) + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::Process - ioctl CEC_DQEVENT failed - errno=%d", errno); + else if (ev.event == CEC_EVENT_STATE_CHANGE) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "CLinuxCECAdapterCommunication::Process - CEC_DQEVENT - CEC_EVENT_STATE_CHANGE - log_addr_mask=%04x phys_addr=%04x", ev.state_change.log_addr_mask, ev.state_change.phys_addr); + + // TODO: handle ev.state_change.log_addr_mask change + + phys_addr = ev.state_change.phys_addr; + phys_addr_changed = true; + + if (ev.state_change.phys_addr == CEC_PHYS_ADDR_INVALID) + { + // Debounce change to invalid physical address with 2 seconds because + // EDID refresh and other events may cause short periods of invalid physical address + phys_addr_timeout.Init(2000); + } + else + { + // Debounce change to valid physical address with 500 ms when no logical address have been claimed + phys_addr_timeout.Init(ev.state_change.log_addr_mask ? 0 : 500); + } + } + } + + if (phys_addr_changed && !phys_addr_timeout.TimeLeft() && !IsStopped()) + { + phys_addr_changed = false; + m_callback->HandlePhysicalAddressChanged(phys_addr); + } + + if (FD_ISSET(m_fd, &rd_fds)) + { + struct cec_msg msg = {}; + if (ioctl(m_fd, CEC_RECEIVE, &msg)) + LIB_CEC->AddLog(CEC_LOG_ERROR, "CLinuxCECAdapterCommunication::Process - ioctl CEC_RECEIVE failed - rx_status=%02x errno=%d", msg.rx_status, errno); + else if (msg.len > 0) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "CLinuxCECAdapterCommunication::Process - ioctl CEC_RECEIVE - rx_status=%02x len=%d addr=%02x opcode=%02x", msg.rx_status, msg.len, msg.msg[0], cec_msg_opcode(&msg)); + + cec_command cmd; + cmd.PushArray(msg.len, msg.msg); + + if (!IsStopped()) + m_callback->OnCommandReceived(cmd); + } + } + + if (!IsStopped()) + Sleep(5); + } + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "CLinuxCECAdapterCommunication::Process - stopped - m_fd=%d", m_fd); + return 0; +} + +#endif diff --git a/src/libcec/adapter/Linux/LinuxCECAdapterCommunication.h b/src/libcec/adapter/Linux/LinuxCECAdapterCommunication.h new file mode 100644 index 00000000..446ba6cc --- /dev/null +++ b/src/libcec/adapter/Linux/LinuxCECAdapterCommunication.h @@ -0,0 +1,96 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC Linux CEC Adapter is Copyright (C) 2017-2018 Jonas Karlman + * based heavily on: + * libCEC AOCEC Code is Copyright (C) 2016 Gerald Dachs + * libCEC Exynos Code is Copyright (C) 2014 Valentin Manea + * libCEC(R) is Copyright (C) 2011-2015 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 + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" + +#if defined(HAVE_LINUX_API) +#include "p8-platform/threads/threads.h" +#include "../AdapterCommunication.h" + +namespace CEC +{ + class CLinuxCECAdapterCommunication : public IAdapterCommunication, public P8PLATFORM::CThread + { + public: + /*! + * @brief Create a new Linux CEC communication handler. + * @param callback The callback to use for incoming CEC commands. + */ + CLinuxCECAdapterCommunication(IAdapterCommunicationCallback *callback); + virtual ~CLinuxCECAdapterCommunication(void); + + /** @name IAdapterCommunication implementation */ + ///{ + bool Open(uint32_t iTimeoutMs = CEC_DEFAULT_CONNECT_TIMEOUT, bool bSkipChecks = false, bool bStartListening = true) override; + void Close(void) override; + bool IsOpen(void) override; + cec_adapter_message_state Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout, bool bIsReply) override; + + bool SetLineTimeout(uint8_t UNUSED(iTimeout)) override { return true; } + bool StartBootloader(void) override { return false; } + bool SetLogicalAddresses(const cec_logical_addresses &addresses) override; + cec_logical_addresses GetLogicalAddresses(void) const override; + bool PingAdapter(void) override { return true; } + uint16_t GetFirmwareVersion(void) override { return 0; } + uint32_t GetFirmwareBuildDate(void) override { return 0; } + bool IsRunningLatestFirmware(void) override { return true; } + bool SetControlledMode(bool UNUSED(controlled)) override { return true; } + bool PersistConfiguration(const libcec_configuration & UNUSED(configuration)) override { return false; } + bool SetAutoMode(bool UNUSED(automode)) override { return false; } + bool GetConfiguration(libcec_configuration & UNUSED(configuration)) override { return false; } + std::string GetPortName(void) override { return std::string("LINUX"); } + uint16_t GetPhysicalAddress(void) override; + cec_vendor_id GetVendorId(void) override; + bool SupportsSourceLogicalAddress(const cec_logical_address address) override { return address > CECDEVICE_TV && address <= CECDEVICE_BROADCAST; } + cec_adapter_type GetAdapterType(void) override { return ADAPTERTYPE_LINUX; } + uint16_t GetAdapterVendorId(void) const override { return 1; } + uint16_t GetAdapterProductId(void) const override { return 1; } + void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) override {} + bool GetStats(struct cec_adapter_stats* UNUSED(stats)) override { return false; } + ///} + + /** @name P8PLATFORM::CThread implementation */ + ///{ + void *Process(void) override; + ///} + + private: + int m_fd; + }; +}; + +#endif diff --git a/src/libcec/adapter/Linux/LinuxCECAdapterDetection.cpp b/src/libcec/adapter/Linux/LinuxCECAdapterDetection.cpp new file mode 100644 index 00000000..7b72238f --- /dev/null +++ b/src/libcec/adapter/Linux/LinuxCECAdapterDetection.cpp @@ -0,0 +1,50 @@ +/* + * This file is part of the libCEC(R) library. + * + * libCEC Linux CEC Adapter is Copyright (C) 2017 Jonas Karlman + * based heavily on: + * libCEC AOCEC Code is Copyright (C) 2016 Gerald Dachs + * libCEC Exynos Code is Copyright (C) 2014 Valentin Manea + * libCEC(R) is Copyright (C) 2011-2015 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 + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" +#include + +#if defined(HAVE_LINUX_API) +#include "LinuxCECAdapterDetection.h" + +using namespace CEC; + +bool CLinuxCECAdapterDetection::FindAdapter(void) +{ + return access(CEC_LINUX_PATH, 0) == 0; +} + +#endif diff --git a/src/libcec/adapter/Linux/LinuxCECAdapterDetection.h b/src/libcec/adapter/Linux/LinuxCECAdapterDetection.h new file mode 100644 index 00000000..f5ea2c47 --- /dev/null +++ b/src/libcec/adapter/Linux/LinuxCECAdapterDetection.h @@ -0,0 +1,51 @@ +#pragma once +/* + * This file is part of the libCEC(R) library. + * + * libCEC Linux CEC Adapter is Copyright (C) 2017 Jonas Karlman + * based heavily on: + * libCEC AOCEC Code is Copyright (C) 2016 Gerald Dachs + * libCEC Exynos Code is Copyright (C) 2014 Valentin Manea + * libCEC(R) is Copyright (C) 2011-2015 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 + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "env.h" + +#if defined(HAVE_LINUX_API) + +namespace CEC +{ + class CLinuxCECAdapterDetection + { + public: + static bool FindAdapter(void); + }; +}; + +#endif diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index e1eacf06..877a3f57 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -9,6 +9,7 @@ # HAVE_RPI_API ON if Raspberry Pi is supported # HAVE_TDA995X_API ON if TDA995X is supported # HAVE_EXYNOS_API ON if Exynos is supported +# HAVE_LINUX_API ON if Linux is supported # HAVE_AOCEC_API ON if AOCEC is supported # HAVE_P8_USB ON if Pulse-Eight devices are supported # HAVE_P8_USB_DETECT ON if Pulse-Eight devices can be auto-detected @@ -30,6 +31,7 @@ SET(HAVE_LIBUDEV OFF CACHE BOOL "udev not supported") SET(HAVE_RPI_API OFF CACHE BOOL "raspberry pi not supported") SET(HAVE_TDA995X_API OFF CACHE BOOL "tda995x not supported") SET(HAVE_EXYNOS_API OFF CACHE BOOL "exynos not supported") +SET(HAVE_LINUX_API OFF CACHE BOOL "linux not supported") SET(HAVE_AOCEC_API OFF CACHE BOOL "aocec not supported") # Pulse-Eight devices are always supported set(HAVE_P8_USB ON CACHE BOOL "p8 usb-cec supported" FORCE) @@ -140,6 +142,16 @@ else() list(APPEND CEC_SOURCES ${CEC_SOURCES_ADAPTER_EXYNOS}) endif() + # Linux + if (${HAVE_LINUX_API}) + set(LIB_INFO "${LIB_INFO}, Linux") + SET(HAVE_LINUX_API ON CACHE BOOL "linux supported" FORCE) + set(CEC_SOURCES_ADAPTER_LINUX adapter/Linux/LinuxCECAdapterDetection.cpp + adapter/Linux/LinuxCECAdapterCommunication.cpp) + source_group("Source Files\\adapter\\Linux" FILES ${CEC_SOURCES_ADAPTER_LINUX}) + list(APPEND CEC_SOURCES ${CEC_SOURCES_ADAPTER_LINUX}) + endif() + # AOCEC if (${HAVE_AOCEC_API}) set(LIB_INFO "${LIB_INFO}, AOCEC") diff --git a/src/libcec/cmake/DisplayPlatformSupport.cmake b/src/libcec/cmake/DisplayPlatformSupport.cmake index 83a778af..f47b1f7b 100644 --- a/src/libcec/cmake/DisplayPlatformSupport.cmake +++ b/src/libcec/cmake/DisplayPlatformSupport.cmake @@ -44,6 +44,12 @@ else() message(STATUS "DRM support: no") endif() +if (HAVE_LINUX_API) + message(STATUS "Linux support: yes") +else() + message(STATUS "Linux support: no") +endif() + if (HAVE_AOCEC_API) message(STATUS "AOCEC support: yes") else() diff --git a/src/libcec/env.h.in b/src/libcec/env.h.in index 456a2e75..71895a86 100644 --- a/src/libcec/env.h.in +++ b/src/libcec/env.h.in @@ -76,6 +76,9 @@ /* Define to 1 for Exynos support */ #cmakedefine HAVE_EXYNOS_API @HAVE_EXYNOS_API@ +/* Define to 1 for Linux support */ +#cmakedefine HAVE_LINUX_API @HAVE_LINUX_API@ + /* Define to 1 for AOCEC support */ #cmakedefine HAVE_AOCEC_API @HAVE_AOCEC_API@ From c0867bbc0727caa09d9dafc7b83ed4f1a4cb74e0 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 1 Apr 2020 23:06:37 +0200 Subject: [PATCH 74/93] changed: wake on boot needed another fw version bump (v10+) --- src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp index fdd7a246..0158cf3f 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp @@ -430,8 +430,8 @@ bool CUSBCECAdapterCommands::SetSettingAutoPowerOn(bool autoOn) { bool bReturn(false); - if (m_persistedConfiguration.iFirmwareVersion < 9) - // only supported by v9+ + if (m_persistedConfiguration.iFirmwareVersion < 10) + // only supported by v10+ return bReturn; /* check whether this value was changed */ @@ -552,7 +552,7 @@ bool CUSBCECAdapterCommands::PersistConfiguration(const libcec_configuration &co bReturn |= SetSettingLogicalAddressMask(CLibCEC::GetMaskForType(configuration.logicalAddresses.primary)); bReturn |= SetSettingPhysicalAddress(configuration.iPhysicalAddress); bReturn |= SetSettingOSDName(configuration.strDeviceName); - if (m_persistedConfiguration.iFirmwareVersion >= 9) + if (m_persistedConfiguration.iFirmwareVersion >= 10) bReturn |= SetSettingAutoPowerOn(configuration.bAutoPowerOn); else bReturn |= SetSettingCECVersion(configuration.cecVersion); @@ -579,7 +579,7 @@ bool CUSBCECAdapterCommands::RequestSettings(void) bReturn |= RequestSettingLogicalAddressMask(); bReturn |= RequestSettingOSDName(); bReturn |= RequestSettingPhysicalAddress(); - if (m_persistedConfiguration.iFirmwareVersion >= 9) + if (m_persistedConfiguration.iFirmwareVersion >= 10) bReturn |= RequestSettingAutoPowerOn(); else bReturn |= RequestSettingCECVersion(); From e4416a0387aed8a5581cc2ec6b0002ed9483b1bb Mon Sep 17 00:00:00 2001 From: wolfgar Date: Thu, 13 Mar 2014 16:03:58 +0100 Subject: [PATCH 75/93] Add iMX6 support Requires specific kernel driver. Check http://stephan-rafin.net/blog/2013/09/30/i-mx6-cec/ Author: wolfgar --- include/cectypes.h | 13 +- src/libcec/CECTypeUtils.h | 2 + src/libcec/adapter/AdapterFactory.cpp | 26 +- src/libcec/adapter/IMX/AdapterMessageQueue.h | 134 +++++++ .../IMX/IMXCECAdapterCommunication.cpp | 328 ++++++++++++++++++ .../adapter/IMX/IMXCECAdapterCommunication.h | 119 +++++++ .../adapter/IMX/IMXCECAdapterDetection.cpp | 42 +++ .../adapter/IMX/IMXCECAdapterDetection.h | 36 ++ 8 files changed, 697 insertions(+), 3 deletions(-) create mode 100644 src/libcec/adapter/IMX/AdapterMessageQueue.h create mode 100644 src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp create mode 100644 src/libcec/adapter/IMX/IMXCECAdapterCommunication.h create mode 100644 src/libcec/adapter/IMX/IMXCECAdapterDetection.cpp create mode 100644 src/libcec/adapter/IMX/IMXCECAdapterDetection.h diff --git a/include/cectypes.h b/include/cectypes.h index 024cc23c..0b27663b 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -301,6 +301,16 @@ namespace CEC { */ #define CEC_AOCEC_VIRTUAL_COM "AOCEC" +/*! + * the path to use for the i.MX CEC wire + */ +#define CEC_IMX_PATH "/dev/mxc_hdmi_cec" + +/*! + * the name of the virtual COM port to use for the i.MX CEC wire + */ +#define CEC_IMX_VIRTUAL_COM "i.MX" + /*! * Mimimum client version */ @@ -875,7 +885,8 @@ typedef enum cec_adapter_type ADAPTERTYPE_TDA995x = 0x200, ADAPTERTYPE_EXYNOS = 0x300, ADAPTERTYPE_LINUX = 0x400, - ADAPTERTYPE_AOCEC = 0x500 + ADAPTERTYPE_AOCEC = 0x500, + ADAPTERTYPE_IMX = 0x600 } cec_adapter_type; /** force exporting through swig */ diff --git a/src/libcec/CECTypeUtils.h b/src/libcec/CECTypeUtils.h index 80c5b3b4..60f35efb 100644 --- a/src/libcec/CECTypeUtils.h +++ b/src/libcec/CECTypeUtils.h @@ -768,6 +768,8 @@ namespace CEC return "Raspberry Pi"; case ADAPTERTYPE_TDA995x: return "TDA995x"; + case ADAPTERTYPE_IMX: + return "i.MX"; case ADAPTERTYPE_LINUX: return "Linux"; default: diff --git a/src/libcec/adapter/AdapterFactory.cpp b/src/libcec/adapter/AdapterFactory.cpp index 323c2724..ede5f375 100644 --- a/src/libcec/adapter/AdapterFactory.cpp +++ b/src/libcec/adapter/AdapterFactory.cpp @@ -68,6 +68,11 @@ #include "AOCEC/AOCECAdapterCommunication.h" #endif +#if defined(HAVE_IMX_API) +#include "IMX/IMXCECAdapterDetection.h" +#include "IMX/IMXCECAdapterCommunication.h" +#endif + using namespace CEC; int8_t CAdapterFactory::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) @@ -160,8 +165,20 @@ int8_t CAdapterFactory::DetectAdapters(cec_adapter_descriptor *deviceList, uint8 } #endif +#if defined(HAVE_IMX_API) + if (iAdaptersFound < iBufSize && CIMXCECAdapterDetection::FindAdapter() && + (!strDevicePath || !strcmp(strDevicePath, CEC_IMX_VIRTUAL_COM))) + { + snprintf(deviceList[iAdaptersFound].strComPath, sizeof(deviceList[iAdaptersFound].strComPath), CEC_IMX_PATH); + snprintf(deviceList[iAdaptersFound].strComName, sizeof(deviceList[iAdaptersFound].strComName), CEC_IMX_VIRTUAL_COM); + deviceList[iAdaptersFound].iVendorId = IMX_ADAPTER_VID; + deviceList[iAdaptersFound].iProductId = IMX_ADAPTER_PID; + deviceList[iAdaptersFound].adapterType = ADAPTERTYPE_IMX; + iAdaptersFound++; + } +#endif -#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_LINUX_API) && !defined(HAVE_AOCEC_API) +#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_EXYNOS_API) && !defined(HAVE_LINUX_API) && !defined(HAVE_AOCEC_API) && !defined(HAVE_IMX_API) #error "libCEC doesn't have support for any type of adapter. please check your build system or configuration" #endif @@ -195,11 +212,16 @@ IAdapterCommunication *CAdapterFactory::GetInstance(const char *strPort, uint16_ return new CRPiCECAdapterCommunication(m_lib->m_cec); #endif +#if defined(HAVE_IMX_API) + if (!strcmp(strPort, CEC_IMX_VIRTUAL_COM)) + return new CIMXCECAdapterCommunication(m_lib->m_cec); +#endif + #if defined(HAVE_P8_USB) return new CUSBCECAdapterCommunication(m_lib->m_cec, strPort, iBaudRate); #endif -#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_EXYNOS_API) && !defined(HAVE_LINUX_API) && !defined(HAVE_AOCEC_API) +#if !defined(HAVE_RPI_API) && !defined(HAVE_P8_USB) && !defined(HAVE_TDA995X_API) && !defined(HAVE_EXYNOS_API) && !defined(HAVE_LINUX_API) && !defined(HAVE_AOCEC_API) && !defined(HAVE_IMX_API) return NULL; #endif } diff --git a/src/libcec/adapter/IMX/AdapterMessageQueue.h b/src/libcec/adapter/IMX/AdapterMessageQueue.h new file mode 100644 index 00000000..45907c4a --- /dev/null +++ b/src/libcec/adapter/IMX/AdapterMessageQueue.h @@ -0,0 +1,134 @@ +#pragma once +/* + * 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 + * http://www.pulse-eight.com/ + * http://www.pulse-eight.net/ + */ + +#include "lib/platform/threads/mutex.h" + +namespace CEC +{ + using namespace PLATFORM; + + class CAdapterMessageQueueEntry + { + public: + CAdapterMessageQueueEntry(const cec_command &command) + : m_bWaiting(true), m_retval((uint32_t)-1), m_bSucceeded(false) + { + m_hash = hashValue( + uint32_t(command.opcode_set ? command.opcode : CEC_OPCODE_NONE), + command.initiator, command.destination); + } + + virtual ~CAdapterMessageQueueEntry(void) {} + + /*! + * @brief Query result from worker thread + */ + uint32_t Result() const + { + return m_retval; + } + + /*! + * @brief Signal waiting threads + */ + void Broadcast(void) + { + CLockObject lock(m_mutex); + m_condition.Broadcast(); + } + + /*! + * @brief Signal waiting thread(s) when message matches this entry + */ + bool CheckMatch(uint32_t opcode, cec_logical_address initiator, + cec_logical_address destination, uint32_t response) + { + uint32_t hash = hashValue(opcode, initiator, destination); + + if (hash == m_hash) + { + CLockObject lock(m_mutex); + + m_retval = response; + m_bSucceeded = true; + m_condition.Signal(); + return true; + } + + return false; + } + + /*! + * @brief Wait for a response to this command. + * @param iTimeout The timeout to use while waiting. + * @return True when a response was received before the timeout passed, false otherwise. + */ + bool Wait(uint32_t iTimeout) + { + CLockObject lock(m_mutex); + + bool bReturn = m_bSucceeded ? true : m_condition.Wait(m_mutex, m_bSucceeded, iTimeout); + m_bWaiting = false; + return bReturn; + } + + /*! + * @return True while a thread is waiting for a signal or isn't waiting yet, false otherwise. + */ + bool IsWaiting(void) + { + CLockObject lock(m_mutex); + return m_bWaiting; + } + + /*! + * @return Hash value for given cec_command + */ + static uint32_t hashValue(uint32_t opcode, + cec_logical_address initiator, + cec_logical_address destination) + { + return 1 | ((uint32_t)initiator << 8) | + ((uint32_t)destination << 16) | ((uint32_t)opcode << 16); + } + + private: + bool m_bWaiting; /**< true while a thread is waiting or when it hasn't started waiting yet */ + PLATFORM::CCondition m_condition; /**< the condition to wait on */ + PLATFORM::CMutex m_mutex; /**< mutex for changes to this class */ + uint32_t m_hash; + uint32_t m_retval; + bool m_bSucceeded; + }; + +}; diff --git a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp new file mode 100644 index 00000000..85136c30 --- /dev/null +++ b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp @@ -0,0 +1,328 @@ +/* + * 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. + * + * IMX adpater port is Copyright (C) 2013 by Stephan Rafin + * + * You can redistribute this file 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. + * + * + */ + +#include "env.h" + +#if defined(HAVE_IMX_API) +#include "IMXCECAdapterCommunication.h" + +#include "lib/CECTypeUtils.h" +#include "lib/LibCEC.h" +#include "lib/platform/sockets/cdevsocket.h" +#include "lib/platform/util/StdString.h" +#include "lib/platform/util/buffer.h" + +/* + * Ioctl definitions from kernel header + */ +#define HDMICEC_IOC_MAGIC 'H' +#define HDMICEC_IOC_SETLOGICALADDRESS _IOW(HDMICEC_IOC_MAGIC, 1, unsigned char) +#define HDMICEC_IOC_STARTDEVICE _IO(HDMICEC_IOC_MAGIC, 2) +#define HDMICEC_IOC_STOPDEVICE _IO(HDMICEC_IOC_MAGIC, 3) +#define HDMICEC_IOC_GETPHYADDRESS _IOR(HDMICEC_IOC_MAGIC, 4, unsigned char[4]) + +#define MAX_CEC_MESSAGE_LEN 17 + +#define MESSAGE_TYPE_RECEIVE_SUCCESS 1 +#define MESSAGE_TYPE_NOACK 2 +#define MESSAGE_TYPE_DISCONNECTED 3 +#define MESSAGE_TYPE_CONNECTED 4 +#define MESSAGE_TYPE_SEND_SUCCESS 5 + +typedef struct hdmi_cec_event{ + int event_type; + int msg_len; + unsigned char msg[MAX_CEC_MESSAGE_LEN]; +}hdmi_cec_event; + + +using namespace std; +using namespace CEC; +using namespace PLATFORM; + +#include "AdapterMessageQueue.h" + +#define LIB_CEC m_callback->GetLib() + +// these are defined in nxp private header file +#define CEC_MSG_SUCCESS 0x00 /*Message transmisson Succeed*/ +#define CEC_CSP_OFF_STATE 0x80 /*CSP in Off State*/ +#define CEC_BAD_REQ_SERVICE 0x81 /*Bad .req service*/ +#define CEC_MSG_FAIL_UNABLE_TO_ACCESS 0x82 /*Message transmisson failed: Unable to access CEC line*/ +#define CEC_MSG_FAIL_ARBITRATION_ERROR 0x83 /*Message transmisson failed: Arbitration error*/ +#define CEC_MSG_FAIL_BIT_TIMMING_ERROR 0x84 /*Message transmisson failed: Bit timming error*/ +#define CEC_MSG_FAIL_DEST_NOT_ACK 0x85 /*Message transmisson failed: Destination Address not aknowledged*/ +#define CEC_MSG_FAIL_DATA_NOT_ACK 0x86 /*Message transmisson failed: Databyte not acknowledged*/ + + +CIMXCECAdapterCommunication::CIMXCECAdapterCommunication(IAdapterCommunicationCallback *callback) : + IAdapterCommunication(callback)/*, + m_bLogicalAddressChanged(false)*/ +{ + CLockObject lock(m_mutex); + + m_iNextMessage = 0; + //m_logicalAddresses.Clear(); + m_logicalAddress = CECDEVICE_UNKNOWN; + m_bLogicalAddressRegistered = false; + m_bInitialised = false; + m_dev = new CCDevSocket(CEC_IMX_PATH); +} + +CIMXCECAdapterCommunication::~CIMXCECAdapterCommunication(void) +{ + Close(); + + CLockObject lock(m_mutex); + delete m_dev; + m_dev = 0; +} + +bool CIMXCECAdapterCommunication::IsOpen(void) +{ + return IsInitialised() && m_dev->IsOpen(); +} + +bool CIMXCECAdapterCommunication::Open(uint32_t iTimeoutMs, bool UNUSED(bSkipChecks), bool bStartListening) +{ + if (m_dev->Open(iTimeoutMs)) + { + if (!bStartListening || CreateThread()) { + if (m_dev->Ioctl(HDMICEC_IOC_STARTDEVICE, NULL) == 0) { + m_bInitialised = true; + return true; + } + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: Unable to start device\n", __func__); + } + m_dev->Close(); + } + + return false; +} + + +void CIMXCECAdapterCommunication::Close(void) +{ + StopThread(0); + + CLockObject lock(m_mutex); + if (!m_bInitialised) { + return; + } + if (m_dev->Ioctl(HDMICEC_IOC_STOPDEVICE, NULL) != 0) { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: Unable to stop device\n", __func__); + } + m_dev->Close(); + m_bInitialised = false; +} + + +std::string CIMXCECAdapterCommunication::GetError(void) const +{ + std::string strError(m_strError); + return strError; +} + + +cec_adapter_message_state CIMXCECAdapterCommunication::Write( + const cec_command &data, bool &UNUSED(bRetry), uint8_t UNUSED(iLineTimeout), bool UNUSED(bIsReply)) +{ + //cec_frame frame; + unsigned char message[MAX_CEC_MESSAGE_LEN]; + int msg_len = 1; + cec_adapter_message_state rc = ADAPTER_MESSAGE_STATE_ERROR; + + if ((size_t)data.parameters.size + data.opcode_set + 1 > sizeof(message)) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: data size too large !", __func__); + return ADAPTER_MESSAGE_STATE_ERROR; + } + + message[0] = (data.initiator << 4) | (data.destination & 0x0f); + if (data.opcode_set) + { + message[1] = data.opcode; + msg_len++; + memcpy(&message[2], data.parameters.data, data.parameters.size); + msg_len+=data.parameters.size; + } + + if (m_dev->Write(message, msg_len) == msg_len) + { + rc = ADAPTER_MESSAGE_STATE_SENT_ACKED; + } + else + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: sent command error !", __func__); + + return rc; +} + + +uint16_t CIMXCECAdapterCommunication::GetFirmwareVersion(void) +{ + /* FIXME add ioctl ? */ + return 0; +} + + +cec_vendor_id CIMXCECAdapterCommunication::GetVendorId(void) +{ + return CEC_VENDOR_UNKNOWN; +} + + +uint16_t CIMXCECAdapterCommunication::GetPhysicalAddress(void) +{ + uint32_t info; + uint16_t phy_addr; + + if (m_dev->Ioctl(HDMICEC_IOC_GETPHYADDRESS, &info) != 0) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: HDMICEC_IOC_GETPHYADDRESS failed !", __func__); + return CEC_INVALID_PHYSICAL_ADDRESS; + } + /* Rebuild 16 bit raw value from fsl 32 bits value */ + phy_addr = ((info & 0x0f) << 12) | (info & 0x0f00) | + ((info & 0x0f0000) >> 12) | ((info & 0x0f000000) >> 24); + + return phy_addr; +} + + +cec_logical_addresses CIMXCECAdapterCommunication::GetLogicalAddresses(void) +{ + cec_logical_addresses addresses; + addresses.Clear(); + + CLockObject lock(m_mutex); + if ((m_logicalAddress & (CECDEVICE_UNKNOWN | CECDEVICE_UNREGISTERED)) == 0) + addresses.Set(m_logicalAddress); + + return addresses; +} + +void CIMXCECAdapterCommunication::HandleLogicalAddressLost(cec_logical_address UNUSED(oldAddress)) +{ + UnregisterLogicalAddress(); +} + +bool CIMXCECAdapterCommunication::UnregisterLogicalAddress(void) +{ + CLockObject lock(m_mutex); + if (!m_bLogicalAddressRegistered) + return true; + + if (m_dev->Ioctl(HDMICEC_IOC_SETLOGICALADDRESS, (void *)CECDEVICE_BROADCAST) != 0) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: HDMICEC_IOC_SETLOGICALADDRESS failed !", __func__); + return false; + } + + m_logicalAddress = CECDEVICE_UNKNOWN; + m_bLogicalAddressRegistered = false; + return true; +} + +bool CIMXCECAdapterCommunication::RegisterLogicalAddress(const cec_logical_address address) +{ + CLockObject lock(m_mutex); + + if (m_logicalAddress == address && m_bLogicalAddressRegistered) + { + return true; + } + + if (m_dev->Ioctl(HDMICEC_IOC_SETLOGICALADDRESS, (void *)address) != 0) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: HDMICEC_IOC_SETLOGICALADDRESS failed !", __func__); + return false; + } + + m_logicalAddress = address; + m_bLogicalAddressRegistered = true; + return true; +} + +bool CIMXCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses) +{ + int log_addr = addresses.primary; + + return RegisterLogicalAddress((cec_logical_address)log_addr); +} + +void *CIMXCECAdapterCommunication::Process(void) +{ + bool bHandled; + hdmi_cec_event event; + int ret; + + uint32_t opcode, status; + cec_logical_address initiator, destination; + + while (!IsStopped()) + { + ret = m_dev->Read((char *)&event, sizeof(event), 5000); + if (ret > 0) + { + + initiator = cec_logical_address(event.msg[0] >> 4); + destination = cec_logical_address(event.msg[0] & 0x0f); + + //LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s: Read data : type : %d initiator %d dest %d", __func__, event.event_type, initiator, destination); + if (event.event_type == MESSAGE_TYPE_RECEIVE_SUCCESS) + /* Message received */ + { + cec_command cmd; + + cec_command::Format( + cmd, initiator, destination, + ( event.msg_len > 1 ) ? cec_opcode(event.msg[1]) : CEC_OPCODE_NONE); + + for( uint8_t i = 2; i < event.msg_len; i++ ) + cmd.parameters.PushBack(event.msg[i]); + + if (!IsStopped()) + m_callback->OnCommandReceived(cmd); + } + + if (event.event_type == MESSAGE_TYPE_CONNECTED) + /* HDMI has just been reconnected - Notify phy address*/ + { + uint16_t iNewAddress = GetPhysicalAddress(); + m_callback->HandlePhysicalAddressChanged(iNewAddress); + } + /* We are not interested in other events */ + } /*else { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s: Read returned %d", __func__, ret); + }*/ + + } + + return 0; +} + +#endif // HAVE_IMX_API diff --git a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h new file mode 100644 index 00000000..6111c5f2 --- /dev/null +++ b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h @@ -0,0 +1,119 @@ +#pragma once +/* + * 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. + * + * IMX adpater port is Copyright (C) 2013 by Stephan Rafin + * + * You can redistribute this file 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. + * + * + */ + +#if defined(HAVE_IMX_API) + +#include "lib/platform/threads/mutex.h" +#include "lib/platform/threads/threads.h" +#include "lib/platform/sockets/socket.h" +#include "lib/adapter/AdapterCommunication.h" +#include + +#define IMX_ADAPTER_VID 0x0471 /*FIXME TBD*/ +#define IMX_ADAPTER_PID 0x1001 + + + +namespace PLATFORM +{ + class CCDevSocket; +}; + + +namespace CEC +{ + class CAdapterMessageQueueEntry; + + class CIMXCECAdapterCommunication : public IAdapterCommunication, public PLATFORM::CThread + { + public: + /*! + * @brief Create a new USB-CEC communication handler. + * @param callback The callback to use for incoming CEC commands. + */ + CIMXCECAdapterCommunication(IAdapterCommunicationCallback *callback); + virtual ~CIMXCECAdapterCommunication(void); + + /** @name IAdapterCommunication implementation */ + ///{ + bool Open(uint32_t iTimeoutMs = CEC_DEFAULT_CONNECT_TIMEOUT, bool bSkipChecks = false, bool bStartListening = true); + void Close(void); + bool IsOpen(void); + std::string GetError(void) const; + cec_adapter_message_state Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout, bool bIsReply); + + bool SetLineTimeout(uint8_t UNUSED(iTimeout)) { return true; } + bool StartBootloader(void) { return false; } + bool SetLogicalAddresses(const cec_logical_addresses &addresses); + cec_logical_addresses GetLogicalAddresses(void); + bool PingAdapter(void) { return IsInitialised(); } + uint16_t GetFirmwareVersion(void); + uint32_t GetFirmwareBuildDate(void) { return 0; } + bool IsRunningLatestFirmware(void) { return true; } + bool PersistConfiguration(const libcec_configuration & UNUSED(configuration)) { return false; } + bool GetConfiguration(libcec_configuration & UNUSED(configuration)) { return false; } + std::string GetPortName(void) { return std::string("IMX"); } + uint16_t GetPhysicalAddress(void); + bool SetControlledMode(bool UNUSED(controlled)) { return true; } + cec_vendor_id GetVendorId(void); + bool SupportsSourceLogicalAddress(const cec_logical_address address) { return address > CECDEVICE_TV && address <= CECDEVICE_BROADCAST; } + cec_adapter_type GetAdapterType(void) { return ADAPTERTYPE_IMX; } + uint16_t GetAdapterVendorId(void) const { return IMX_ADAPTER_VID; } + uint16_t GetAdapterProductId(void) const { return IMX_ADAPTER_PID; } + void HandleLogicalAddressLost(cec_logical_address UNUSED(oldAddress)); + void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} + bool RegisterLogicalAddress(const cec_logical_address address); + ///} + + /** @name PLATFORM::CThread implementation */ + ///{ + void *Process(void); + ///} + + private: + bool IsInitialised(void) const { return m_bInitialised; }; + bool UnregisterLogicalAddress(void); + + std::string m_strError; /**< current error message */ + + //cec_logical_addresses m_logicalAddresses; + cec_logical_address m_logicalAddress; + + PLATFORM::CMutex m_mutex; + PLATFORM::CCDevSocket *m_dev; /**< the device connection */ + bool m_bLogicalAddressRegistered; + bool m_bInitialised; + + PLATFORM::CMutex m_messageMutex; + uint32_t m_iNextMessage; + std::map m_messages; + }; + +}; + +#endif diff --git a/src/libcec/adapter/IMX/IMXCECAdapterDetection.cpp b/src/libcec/adapter/IMX/IMXCECAdapterDetection.cpp new file mode 100644 index 00000000..dc4dca02 --- /dev/null +++ b/src/libcec/adapter/IMX/IMXCECAdapterDetection.cpp @@ -0,0 +1,42 @@ +/* + * 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. + * + * IMX adpater port is Copyright (C) 2013 by Stephan Rafin + * + * You can redistribute this file 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. + * + * + */ + +#include "env.h" +#include + +#if defined(HAVE_IMX_API) +#include "IMXCECAdapterDetection.h" + + +using namespace CEC; + +bool CIMXCECAdapterDetection::FindAdapter(void) +{ + return access(CEC_IMX_PATH, 0) == 0; +} + +#endif diff --git a/src/libcec/adapter/IMX/IMXCECAdapterDetection.h b/src/libcec/adapter/IMX/IMXCECAdapterDetection.h new file mode 100644 index 00000000..14af8d86 --- /dev/null +++ b/src/libcec/adapter/IMX/IMXCECAdapterDetection.h @@ -0,0 +1,36 @@ +#pragma once +/* + * 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. + * + * IMX adpater port is Copyright (C) 2013 by Stephan Rafin + * + * You can redistribute this file 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. + * + * + */ + +namespace CEC +{ + class CIMXCECAdapterDetection + { + public: + static bool FindAdapter(void); + }; +} From b9a17f9d9f894bff5310a257aa16128f51c375cb Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 6 Nov 2016 09:07:27 +0100 Subject: [PATCH 76/93] iMX6: Adapt to mainline updates --- src/libcec/CMakeLists.txt | 3 +++ src/libcec/adapter/IMX/AdapterMessageQueue.h | 9 +++---- .../IMX/IMXCECAdapterCommunication.cpp | 15 +++++------- .../adapter/IMX/IMXCECAdapterCommunication.h | 24 +++++++++---------- src/libcec/cmake/CheckPlatformSupport.cmake | 13 ++++++++++ src/libcec/cmake/DisplayPlatformSupport.cmake | 6 +++++ src/libcec/env.h.in | 3 +++ 7 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/libcec/CMakeLists.txt b/src/libcec/CMakeLists.txt index 74fe5f37..cd0b81d1 100644 --- a/src/libcec/CMakeLists.txt +++ b/src/libcec/CMakeLists.txt @@ -107,6 +107,9 @@ set(CEC_HEADERS devices/CECRecordingDevice.h adapter/RPi/RPiCECAdapterMessageQueue.h adapter/RPi/RPiCECAdapterCommunication.h adapter/RPi/RPiCECAdapterDetection.h + adapter/IMX/AdapterMessageQueue.h + adapter/IMX/IMXCECAdapterCommunication.h + adapter/IMX/IMXCECAdapterDetection.h CECInputBuffer.h platform/util/baudrate.h platform/util/edid.h diff --git a/src/libcec/adapter/IMX/AdapterMessageQueue.h b/src/libcec/adapter/IMX/AdapterMessageQueue.h index 45907c4a..f4d57083 100644 --- a/src/libcec/adapter/IMX/AdapterMessageQueue.h +++ b/src/libcec/adapter/IMX/AdapterMessageQueue.h @@ -31,11 +31,12 @@ * http://www.pulse-eight.net/ */ -#include "lib/platform/threads/mutex.h" +#include "env.h" +#include "p8-platform/threads/mutex.h" namespace CEC { - using namespace PLATFORM; + using namespace P8PLATFORM; class CAdapterMessageQueueEntry { @@ -124,8 +125,8 @@ namespace CEC private: bool m_bWaiting; /**< true while a thread is waiting or when it hasn't started waiting yet */ - PLATFORM::CCondition m_condition; /**< the condition to wait on */ - PLATFORM::CMutex m_mutex; /**< mutex for changes to this class */ + P8PLATFORM::CCondition m_condition; /**< the condition to wait on */ + P8PLATFORM::CMutex m_mutex; /**< mutex for changes to this class */ uint32_t m_hash; uint32_t m_retval; bool m_bSucceeded; diff --git a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp index 85136c30..eea6cb24 100644 --- a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp +++ b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp @@ -30,11 +30,9 @@ #if defined(HAVE_IMX_API) #include "IMXCECAdapterCommunication.h" -#include "lib/CECTypeUtils.h" -#include "lib/LibCEC.h" -#include "lib/platform/sockets/cdevsocket.h" -#include "lib/platform/util/StdString.h" -#include "lib/platform/util/buffer.h" +#include "p8-platform/sockets/cdevsocket.h" +#include "CECTypeUtils.h" +#include "LibCEC.h" /* * Ioctl definitions from kernel header @@ -62,7 +60,7 @@ typedef struct hdmi_cec_event{ using namespace std; using namespace CEC; -using namespace PLATFORM; +using namespace P8PLATFORM; #include "AdapterMessageQueue.h" @@ -213,7 +211,7 @@ uint16_t CIMXCECAdapterCommunication::GetPhysicalAddress(void) } -cec_logical_addresses CIMXCECAdapterCommunication::GetLogicalAddresses(void) +cec_logical_addresses CIMXCECAdapterCommunication::GetLogicalAddresses(void) const { cec_logical_addresses addresses; addresses.Clear(); @@ -276,11 +274,10 @@ bool CIMXCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresse void *CIMXCECAdapterCommunication::Process(void) { - bool bHandled; hdmi_cec_event event; int ret; - uint32_t opcode, status; + //uint32_t opcode, status; cec_logical_address initiator, destination; while (!IsStopped()) diff --git a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h index 6111c5f2..1fa3845a 100644 --- a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h +++ b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h @@ -28,10 +28,10 @@ #if defined(HAVE_IMX_API) -#include "lib/platform/threads/mutex.h" -#include "lib/platform/threads/threads.h" -#include "lib/platform/sockets/socket.h" -#include "lib/adapter/AdapterCommunication.h" +#include "p8-platform/threads/mutex.h" +#include "p8-platform/threads/threads.h" +#include "p8-platform/sockets/socket.h" +#include "adapter/AdapterCommunication.h" #include #define IMX_ADAPTER_VID 0x0471 /*FIXME TBD*/ @@ -39,7 +39,7 @@ -namespace PLATFORM +namespace P8PLATFORM { class CCDevSocket; }; @@ -49,7 +49,7 @@ namespace CEC { class CAdapterMessageQueueEntry; - class CIMXCECAdapterCommunication : public IAdapterCommunication, public PLATFORM::CThread + class CIMXCECAdapterCommunication : public IAdapterCommunication, public P8PLATFORM::CThread { public: /*! @@ -70,7 +70,7 @@ namespace CEC bool SetLineTimeout(uint8_t UNUSED(iTimeout)) { return true; } bool StartBootloader(void) { return false; } bool SetLogicalAddresses(const cec_logical_addresses &addresses); - cec_logical_addresses GetLogicalAddresses(void); + cec_logical_addresses GetLogicalAddresses(void) const; bool PingAdapter(void) { return IsInitialised(); } uint16_t GetFirmwareVersion(void); uint32_t GetFirmwareBuildDate(void) { return 0; } @@ -90,7 +90,7 @@ namespace CEC bool RegisterLogicalAddress(const cec_logical_address address); ///} - /** @name PLATFORM::CThread implementation */ + /** @name P8PLATFORM::CThread implementation */ ///{ void *Process(void); ///} @@ -101,15 +101,15 @@ namespace CEC std::string m_strError; /**< current error message */ - //cec_logical_addresses m_logicalAddresses; + //cec_logical_addresses m_logicalAddresses; cec_logical_address m_logicalAddress; - PLATFORM::CMutex m_mutex; - PLATFORM::CCDevSocket *m_dev; /**< the device connection */ + mutable P8PLATFORM::CMutex m_mutex; + P8PLATFORM::CCDevSocket *m_dev; /**< the device connection */ bool m_bLogicalAddressRegistered; bool m_bInitialised; - PLATFORM::CMutex m_messageMutex; + P8PLATFORM::CMutex m_messageMutex; uint32_t m_iNextMessage; std::map m_messages; }; diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index 877a3f57..c585d065 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -11,6 +11,7 @@ # HAVE_EXYNOS_API ON if Exynos is supported # HAVE_LINUX_API ON if Linux is supported # HAVE_AOCEC_API ON if AOCEC is supported +# HAVE_IMX_API ON if iMX.6 is supported # HAVE_P8_USB ON if Pulse-Eight devices are supported # HAVE_P8_USB_DETECT ON if Pulse-Eight devices can be auto-detected # HAVE_DRM_EDID_PARSER ON if DRM EDID parsing is supported @@ -163,6 +164,18 @@ else() else() set(HAVE_AOCEC_API 0) endif() + + # i.MX6 + if (${HAVE_IMX_API}) + set(LIB_INFO "${LIB_INFO}, 'i.MX6'") + set(HAVE_IMX_API 1) + set(CEC_SOURCES_ADAPTER_IMX adapter/IMX/IMXCECAdapterCommunication.cpp + adapter/IMX/IMXCECAdapterDetection.cpp) + source_group("Source Files\\adapter\\IMX" FILES ${CEC_SOURCES_ADAPTER_IMX}) + list(APPEND CEC_SOURCES ${CEC_SOURCES_ADAPTER_IMX}) + else() + set(HAVE_IMX_API 0) + endif() endif() # rt diff --git a/src/libcec/cmake/DisplayPlatformSupport.cmake b/src/libcec/cmake/DisplayPlatformSupport.cmake index f47b1f7b..8b2a558f 100644 --- a/src/libcec/cmake/DisplayPlatformSupport.cmake +++ b/src/libcec/cmake/DisplayPlatformSupport.cmake @@ -56,6 +56,12 @@ else() message(STATUS "AOCEC support: no") endif() +if (HAVE_IMX_API) + message(STATUS "i.MX6 support: yes") +else() + message(STATUS "i.MX6 support: no") +endif() + if (HAVE_PYTHON) message(STATUS "Python support: version ${PYTHONLIBS_VERSION_STRING} (${PYTHON_VERSION})") else() diff --git a/src/libcec/env.h.in b/src/libcec/env.h.in index 71895a86..5f5fde5d 100644 --- a/src/libcec/env.h.in +++ b/src/libcec/env.h.in @@ -73,6 +73,9 @@ /* Define to 1 for TDA995x support */ #cmakedefine HAVE_TDA995X_API @HAVE_TDA995X_API@ +/* Define to 1 for IMX support */ +#cmakedefine HAVE_IMX_API @HAVE_IMX_API@ + /* Define to 1 for Exynos support */ #cmakedefine HAVE_EXYNOS_API @HAVE_EXYNOS_API@ From efe0c864d76a19b3306263827d8509fecf88a982 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 6 Nov 2016 09:09:00 +0100 Subject: [PATCH 77/93] iMX6: Remove unneeded variables and classes, move ioctl defines to separate header --- src/libcec/CMakeLists.txt | 1 - src/libcec/adapter/IMX/AdapterMessageQueue.h | 135 ------------------ src/libcec/adapter/IMX/IMXCEC.h | 52 +++++++ .../IMX/IMXCECAdapterCommunication.cpp | 41 +----- .../adapter/IMX/IMXCECAdapterCommunication.h | 4 - 5 files changed, 54 insertions(+), 179 deletions(-) delete mode 100644 src/libcec/adapter/IMX/AdapterMessageQueue.h create mode 100644 src/libcec/adapter/IMX/IMXCEC.h diff --git a/src/libcec/CMakeLists.txt b/src/libcec/CMakeLists.txt index cd0b81d1..638add8f 100644 --- a/src/libcec/CMakeLists.txt +++ b/src/libcec/CMakeLists.txt @@ -107,7 +107,6 @@ set(CEC_HEADERS devices/CECRecordingDevice.h adapter/RPi/RPiCECAdapterMessageQueue.h adapter/RPi/RPiCECAdapterCommunication.h adapter/RPi/RPiCECAdapterDetection.h - adapter/IMX/AdapterMessageQueue.h adapter/IMX/IMXCECAdapterCommunication.h adapter/IMX/IMXCECAdapterDetection.h CECInputBuffer.h diff --git a/src/libcec/adapter/IMX/AdapterMessageQueue.h b/src/libcec/adapter/IMX/AdapterMessageQueue.h deleted file mode 100644 index f4d57083..00000000 --- a/src/libcec/adapter/IMX/AdapterMessageQueue.h +++ /dev/null @@ -1,135 +0,0 @@ -#pragma once -/* - * 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 - * http://www.pulse-eight.com/ - * http://www.pulse-eight.net/ - */ - -#include "env.h" -#include "p8-platform/threads/mutex.h" - -namespace CEC -{ - using namespace P8PLATFORM; - - class CAdapterMessageQueueEntry - { - public: - CAdapterMessageQueueEntry(const cec_command &command) - : m_bWaiting(true), m_retval((uint32_t)-1), m_bSucceeded(false) - { - m_hash = hashValue( - uint32_t(command.opcode_set ? command.opcode : CEC_OPCODE_NONE), - command.initiator, command.destination); - } - - virtual ~CAdapterMessageQueueEntry(void) {} - - /*! - * @brief Query result from worker thread - */ - uint32_t Result() const - { - return m_retval; - } - - /*! - * @brief Signal waiting threads - */ - void Broadcast(void) - { - CLockObject lock(m_mutex); - m_condition.Broadcast(); - } - - /*! - * @brief Signal waiting thread(s) when message matches this entry - */ - bool CheckMatch(uint32_t opcode, cec_logical_address initiator, - cec_logical_address destination, uint32_t response) - { - uint32_t hash = hashValue(opcode, initiator, destination); - - if (hash == m_hash) - { - CLockObject lock(m_mutex); - - m_retval = response; - m_bSucceeded = true; - m_condition.Signal(); - return true; - } - - return false; - } - - /*! - * @brief Wait for a response to this command. - * @param iTimeout The timeout to use while waiting. - * @return True when a response was received before the timeout passed, false otherwise. - */ - bool Wait(uint32_t iTimeout) - { - CLockObject lock(m_mutex); - - bool bReturn = m_bSucceeded ? true : m_condition.Wait(m_mutex, m_bSucceeded, iTimeout); - m_bWaiting = false; - return bReturn; - } - - /*! - * @return True while a thread is waiting for a signal or isn't waiting yet, false otherwise. - */ - bool IsWaiting(void) - { - CLockObject lock(m_mutex); - return m_bWaiting; - } - - /*! - * @return Hash value for given cec_command - */ - static uint32_t hashValue(uint32_t opcode, - cec_logical_address initiator, - cec_logical_address destination) - { - return 1 | ((uint32_t)initiator << 8) | - ((uint32_t)destination << 16) | ((uint32_t)opcode << 16); - } - - private: - bool m_bWaiting; /**< true while a thread is waiting or when it hasn't started waiting yet */ - P8PLATFORM::CCondition m_condition; /**< the condition to wait on */ - P8PLATFORM::CMutex m_mutex; /**< mutex for changes to this class */ - uint32_t m_hash; - uint32_t m_retval; - bool m_bSucceeded; - }; - -}; diff --git a/src/libcec/adapter/IMX/IMXCEC.h b/src/libcec/adapter/IMX/IMXCEC.h new file mode 100644 index 00000000..f3970544 --- /dev/null +++ b/src/libcec/adapter/IMX/IMXCEC.h @@ -0,0 +1,52 @@ +#pragma once +/* + * 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. + * + * IMX adpater port is Copyright (C) 2013 by Stephan Rafin + * + * You can redistribute this file 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. + * + * + */ + + +/* + * Ioctl definitions from kernel header + */ +#define HDMICEC_IOC_MAGIC 'H' +#define HDMICEC_IOC_SETLOGICALADDRESS _IOW(HDMICEC_IOC_MAGIC, 1, unsigned char) +#define HDMICEC_IOC_STARTDEVICE _IO(HDMICEC_IOC_MAGIC, 2) +#define HDMICEC_IOC_STOPDEVICE _IO(HDMICEC_IOC_MAGIC, 3) +#define HDMICEC_IOC_GETPHYADDRESS _IOR(HDMICEC_IOC_MAGIC, 4, unsigned char[4]) + +#define MESSAGE_TYPE_RECEIVE_SUCCESS 1 +#define MESSAGE_TYPE_NOACK 2 +#define MESSAGE_TYPE_DISCONNECTED 3 +#define MESSAGE_TYPE_CONNECTED 4 +#define MESSAGE_TYPE_SEND_SUCCESS 5 + +#define MAX_CEC_MESSAGE_LEN 17 + +typedef struct hdmi_cec_event { + int event_type; + int msg_len; + unsigned char msg[MAX_CEC_MESSAGE_LEN]; +} hdmi_cec_event; + diff --git a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp index eea6cb24..1cfff784 100644 --- a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp +++ b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp @@ -34,57 +34,21 @@ #include "CECTypeUtils.h" #include "LibCEC.h" -/* - * Ioctl definitions from kernel header - */ -#define HDMICEC_IOC_MAGIC 'H' -#define HDMICEC_IOC_SETLOGICALADDRESS _IOW(HDMICEC_IOC_MAGIC, 1, unsigned char) -#define HDMICEC_IOC_STARTDEVICE _IO(HDMICEC_IOC_MAGIC, 2) -#define HDMICEC_IOC_STOPDEVICE _IO(HDMICEC_IOC_MAGIC, 3) -#define HDMICEC_IOC_GETPHYADDRESS _IOR(HDMICEC_IOC_MAGIC, 4, unsigned char[4]) - -#define MAX_CEC_MESSAGE_LEN 17 - -#define MESSAGE_TYPE_RECEIVE_SUCCESS 1 -#define MESSAGE_TYPE_NOACK 2 -#define MESSAGE_TYPE_DISCONNECTED 3 -#define MESSAGE_TYPE_CONNECTED 4 -#define MESSAGE_TYPE_SEND_SUCCESS 5 - -typedef struct hdmi_cec_event{ - int event_type; - int msg_len; - unsigned char msg[MAX_CEC_MESSAGE_LEN]; -}hdmi_cec_event; - +#include "IMXCEC.h" using namespace std; using namespace CEC; using namespace P8PLATFORM; -#include "AdapterMessageQueue.h" - #define LIB_CEC m_callback->GetLib() -// these are defined in nxp private header file -#define CEC_MSG_SUCCESS 0x00 /*Message transmisson Succeed*/ -#define CEC_CSP_OFF_STATE 0x80 /*CSP in Off State*/ -#define CEC_BAD_REQ_SERVICE 0x81 /*Bad .req service*/ -#define CEC_MSG_FAIL_UNABLE_TO_ACCESS 0x82 /*Message transmisson failed: Unable to access CEC line*/ -#define CEC_MSG_FAIL_ARBITRATION_ERROR 0x83 /*Message transmisson failed: Arbitration error*/ -#define CEC_MSG_FAIL_BIT_TIMMING_ERROR 0x84 /*Message transmisson failed: Bit timming error*/ -#define CEC_MSG_FAIL_DEST_NOT_ACK 0x85 /*Message transmisson failed: Destination Address not aknowledged*/ -#define CEC_MSG_FAIL_DATA_NOT_ACK 0x86 /*Message transmisson failed: Databyte not acknowledged*/ - CIMXCECAdapterCommunication::CIMXCECAdapterCommunication(IAdapterCommunicationCallback *callback) : - IAdapterCommunication(callback)/*, - m_bLogicalAddressChanged(false)*/ + IAdapterCommunication(callback) { CLockObject lock(m_mutex); m_iNextMessage = 0; - //m_logicalAddresses.Clear(); m_logicalAddress = CECDEVICE_UNKNOWN; m_bLogicalAddressRegistered = false; m_bInitialised = false; @@ -277,7 +241,6 @@ void *CIMXCECAdapterCommunication::Process(void) hdmi_cec_event event; int ret; - //uint32_t opcode, status; cec_logical_address initiator, destination; while (!IsStopped()) diff --git a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h index 1fa3845a..8b5dcee1 100644 --- a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h +++ b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h @@ -47,8 +47,6 @@ namespace P8PLATFORM namespace CEC { - class CAdapterMessageQueueEntry; - class CIMXCECAdapterCommunication : public IAdapterCommunication, public P8PLATFORM::CThread { public: @@ -101,7 +99,6 @@ namespace CEC std::string m_strError; /**< current error message */ - //cec_logical_addresses m_logicalAddresses; cec_logical_address m_logicalAddress; mutable P8PLATFORM::CMutex m_mutex; @@ -111,7 +108,6 @@ namespace CEC P8PLATFORM::CMutex m_messageMutex; uint32_t m_iNextMessage; - std::map m_messages; }; }; From 498b277d96b1da7480e302cbc85c2b80d348f3e5 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 6 Nov 2016 19:30:02 +0100 Subject: [PATCH 78/93] iMX6: Fix write error handling, return firmware version 1 --- .../IMX/IMXCECAdapterCommunication.cpp | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp index 1cfff784..13d87ff2 100644 --- a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp +++ b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp @@ -113,10 +113,8 @@ std::string CIMXCECAdapterCommunication::GetError(void) const cec_adapter_message_state CIMXCECAdapterCommunication::Write( const cec_command &data, bool &UNUSED(bRetry), uint8_t UNUSED(iLineTimeout), bool UNUSED(bIsReply)) { - //cec_frame frame; + int error, msg_len = 1; unsigned char message[MAX_CEC_MESSAGE_LEN]; - int msg_len = 1; - cec_adapter_message_state rc = ADAPTER_MESSAGE_STATE_ERROR; if ((size_t)data.parameters.size + data.opcode_set + 1 > sizeof(message)) { @@ -134,20 +132,23 @@ cec_adapter_message_state CIMXCECAdapterCommunication::Write( } if (m_dev->Write(message, msg_len) == msg_len) - { - rc = ADAPTER_MESSAGE_STATE_SENT_ACKED; - } - else - LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: sent command error !", __func__); + return ADAPTER_MESSAGE_STATE_SENT_ACKED; + + error = m_dev->GetErrorNumber(); + if (error == EIO) + return ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED; - return rc; + if (error != EAGAIN) + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: write error %d", __func__, error); + + return ADAPTER_MESSAGE_STATE_ERROR; } uint16_t CIMXCECAdapterCommunication::GetFirmwareVersion(void) { /* FIXME add ioctl ? */ - return 0; + return 1; } From e4b08f3c76d62f8f431b755eaebb78e47c7015af Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 6 Nov 2016 21:47:51 +0100 Subject: [PATCH 79/93] iMX6: Reduce read timeout --- src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp index 13d87ff2..255feed2 100644 --- a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp +++ b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp @@ -246,7 +246,7 @@ void *CIMXCECAdapterCommunication::Process(void) while (!IsStopped()) { - ret = m_dev->Read((char *)&event, sizeof(event), 5000); + ret = m_dev->Read((char *)&event, sizeof(event), 1000); if (ret > 0) { From bb7cab764827c0fe42615d1e271b541e98962123 Mon Sep 17 00:00:00 2001 From: Rudi Date: Tue, 4 Dec 2018 21:03:37 +0100 Subject: [PATCH 80/93] CIMXCECAdapterCommunication: Add (dummy-)implementations for SetAutoMode() and GetStats() --- src/libcec/adapter/IMX/IMXCECAdapterCommunication.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h index 8b5dcee1..43d38032 100644 --- a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h +++ b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h @@ -75,6 +75,7 @@ namespace CEC bool IsRunningLatestFirmware(void) { return true; } bool PersistConfiguration(const libcec_configuration & UNUSED(configuration)) { return false; } bool GetConfiguration(libcec_configuration & UNUSED(configuration)) { return false; } + bool SetAutoMode(bool UNUSED(automode)) { return false; } std::string GetPortName(void) { return std::string("IMX"); } uint16_t GetPhysicalAddress(void); bool SetControlledMode(bool UNUSED(controlled)) { return true; } @@ -85,7 +86,7 @@ namespace CEC uint16_t GetAdapterProductId(void) const { return IMX_ADAPTER_PID; } void HandleLogicalAddressLost(cec_logical_address UNUSED(oldAddress)); void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} - bool RegisterLogicalAddress(const cec_logical_address address); + bool GetStats(struct cec_adapter_stats* UNUSED(stats)) { return false; } ///} /** @name P8PLATFORM::CThread implementation */ @@ -96,6 +97,7 @@ namespace CEC private: bool IsInitialised(void) const { return m_bInitialised; }; bool UnregisterLogicalAddress(void); + bool RegisterLogicalAddress(const cec_logical_address address); std::string m_strError; /**< current error message */ From 818a0eafb09313f42f808f6264b6cdc3a0abb023 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 8 Apr 2020 01:55:08 +0200 Subject: [PATCH 81/93] fixed: replace SWIG_ADD_MODULE and force python2 on win x86 eventghost still uses python2/x86 closes #481 --- CMakeLists.txt | 2 +- src/cec-client/CMakeLists.txt | 2 +- src/cecc-client/CMakeLists.txt | 2 +- src/libcec/CMakeLists.txt | 3 +- src/libcec/cmake/CheckPlatformSupport.cmake | 51 ++++++++++++++++----- src/libcec/cmake/SetBuildInfo.cmake | 2 +- src/pyCecClient/CMakeLists.txt | 18 ++++++-- 7 files changed, 58 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f1ebaea5..8fdfc650 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ project(libcec) -cmake_minimum_required(VERSION 2.8.11) +cmake_minimum_required(VERSION 3.12.0) set(LIBCEC_VERSION_MAJOR 4) set(LIBCEC_VERSION_MINOR 0) diff --git a/src/cec-client/CMakeLists.txt b/src/cec-client/CMakeLists.txt index 8002d49a..079acd59 100644 --- a/src/cec-client/CMakeLists.txt +++ b/src/cec-client/CMakeLists.txt @@ -1,5 +1,5 @@ project(cecclient) -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 3.12.0) set(cecclient_NAME cecclient) set(cecclient_DESCRIPTION "libCEC test client") diff --git a/src/cecc-client/CMakeLists.txt b/src/cecc-client/CMakeLists.txt index 3b8b9e86..b0ee0115 100644 --- a/src/cecc-client/CMakeLists.txt +++ b/src/cecc-client/CMakeLists.txt @@ -1,5 +1,5 @@ project(ceccclient) -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 3.12.0) set(ceccclient_NAME ceccclient) set(ceccclient_DESCRIPTION "libCEC test client") diff --git a/src/libcec/CMakeLists.txt b/src/libcec/CMakeLists.txt index 638add8f..e3718b40 100644 --- a/src/libcec/CMakeLists.txt +++ b/src/libcec/CMakeLists.txt @@ -1,6 +1,5 @@ project(cec) - -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 3.12.0) set(cec_NAME cec) set(cec_DESCRIPTION "libCEC") diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index c585d065..755d9cf3 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -46,13 +46,24 @@ if(WIN32) # Windows add_definitions(-DTARGET_WINDOWS -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -D_WINSOCKAPI_) set(LIB_DESTINATION ".") - check_symbol_exists(_X64_ Windows.h WIN64) - check_symbol_exists(_AMD64_ Windows.h AMD64) - if (DEFINED WIN64 OR DEFINED AMD64) - set(LIB_INFO "${LIB_INFO} (x64)") - else() + + if("${MSVC_C_ARCHITECTURE_ID}" STREQUAL "X86") + set(LIB_INFO "${LIB_INFO} (x86)") add_definitions(-D_USE_32BIT_TIME_T) + # force python2 for eventghost + set(PYTHON_USE_VERSION 2) + elseif("${MSVC_C_ARCHITECTURE_ID}" STREQUAL "x64") + check_symbol_exists(_X64_ Windows.h WIN64) + check_symbol_exists(_AMD64_ Windows.h AMD64) + if (DEFINED WIN64 OR DEFINED AMD64) + set(LIB_INFO "${LIB_INFO} (x64)") + endif() + elseif("${MSVC_C_ARCHITECTURE_ID}" STREQUAL "ARM") + set(LIB_INFO "${LIB_INFO} (arm)") + else() + message(FATAL_ERROR "Unknown architecture id: ${MSVC_C_ARCHITECTURE_ID}") endif() + set(HAVE_P8_USB_DETECT ON CACHE BOOL "p8 usb-cec detection supported" FORCE) set(LIB_INFO "${LIB_INFO}, features: P8_USB, P8_detect") @@ -190,14 +201,31 @@ if (${SKIP_PYTHON_WRAPPER}) message(STATUS "Not generating Python wrapper") else() # Python - include(FindPythonLibs) - find_package(PythonLibs) + if(PYTHON_USE_VERSION EQUAL 2) + # forced v2 + include(FindPython2) + find_package(Python2 COMPONENTS Interpreter Development) + set(PYTHONLIBS_FOUND "${Python2_FOUND}") + set(PYTHONLIBS_VERSION_STRING "${Python2_VERSION}") + set(PYTHON_INCLUDE_PATH "${Python2_INCLUDE_DIRS}") + set(PYTHON_LIBRARIES "${Python2_LIBRARIES}") + else() + include(FindPythonLibs) + find_package(PythonLibs) + endif() # Swig find_package(SWIG) if (PYTHONLIBS_FOUND AND SWIG_FOUND) - set(CMAKE_SWIG_FLAGS "-threads") set(HAVE_PYTHON 1) + + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") + # old style swig + cmake_policy(SET CMP0078 OLD) + cmake_policy(SET CMP0086 OLD) + endif() + + set(CMAKE_SWIG_FLAGS "-threads") if ("${PYTHONLIBS_VERSION_STRING}" STREQUAL "") message(STATUS "Python version not found, defaulting to 2.7") set(PYTHONLIBS_VERSION_STRING "2.7.x") @@ -213,13 +241,12 @@ else() include_directories(${CMAKE_CURRENT_SOURCE_DIR}) SET_SOURCE_FILES_PROPERTIES(libcec.i PROPERTIES CPLUSPLUS ON) - swig_add_module(cec python libcec.i) - swig_link_libraries(cec ${PYTHON_LIBRARIES}) - swig_link_libraries(cec cec) + SWIG_ADD_LIBRARY(cec LANGUAGE python TYPE MODULE SOURCES libcec.i) + SWIG_LINK_LIBRARIES(cec cec ${PYTHON_LIBRARIES}) SET(PYTHON_LIB_INSTALL_PATH "/cec" CACHE STRING "python lib path") if (${PYTHON_MAJOR_VERSION} EQUAL 2 AND ${PYTHON_MINOR_VERSION} GREATER 6) - SET(PYTHON_LIB_INSTALL_PATH "" CACHE STRING "python lib path" FORCE) + SET(PYTHON_LIB_INSTALL_PATH "" CACHE STRING "python lib path" FORCE) else() if (${PYTHON_MAJOR_VERSION} GREATER 2) SET(PYTHON_LIB_INSTALL_PATH "" CACHE STRING "python lib path" FORCE) diff --git a/src/libcec/cmake/SetBuildInfo.cmake b/src/libcec/cmake/SetBuildInfo.cmake index 839228ef..3402f9f8 100644 --- a/src/libcec/cmake/SetBuildInfo.cmake +++ b/src/libcec/cmake/SetBuildInfo.cmake @@ -7,7 +7,7 @@ if(WIN32) # Windows - set(LIB_INFO "compiled on ${CMAKE_SYSTEM}") + set(LIB_INFO "compiled using MSVC ${CMAKE_CXX_COMPILER_VERSION}") else() # not Windows diff --git a/src/pyCecClient/CMakeLists.txt b/src/pyCecClient/CMakeLists.txt index 91ef1118..f0e5f63c 100644 --- a/src/pyCecClient/CMakeLists.txt +++ b/src/pyCecClient/CMakeLists.txt @@ -1,11 +1,21 @@ project(pyCecClient) -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 3.12.0) # Python -include(FindPythonLibs) -find_package(PythonLibs) +if(WIN32 AND "${MSVC_C_ARCHITECTURE_ID}" STREQUAL "X86") + set(PYTHON_USE_VERSION 2) +endif() + +if(PYTHON_USE_VERSION EQUAL 2) + include(FindPython2) + find_package(Python2) + set(PYTHONLIBS_FOUND "${Python2_FOUND}") +else() + include(FindPythonLibs) + find_package(PythonLibs) +endif() -if (PYTHONLIBS_FOUND) +if(PYTHONLIBS_FOUND) if (WIN32) install(PROGRAMS pyCecClient.py DESTINATION python/.) From 844fd89ea3b12f9e83f0dad0a5aa9073923dcca3 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 8 Apr 2020 01:57:18 +0200 Subject: [PATCH 82/93] fixed: cb_cec_log_message() formatting in cecc-client --- src/cecc-client/cecc-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cecc-client/cecc-client.c b/src/cecc-client/cecc-client.c index e846b1bb..d65b77fc 100644 --- a/src/cecc-client/cecc-client.c +++ b/src/cecc-client/cecc-client.c @@ -109,7 +109,7 @@ static void cb_cec_log_message(void* lib, const cec_log_message* message) break; } - printf("%s[%" PRId64 "]\t%s\n", strLevel, message->time, message->message); + printf("%s[" PRId64 "]\t%s\n", strLevel, message->time, message->message); } } From a19a6dd436183fb2abb67593fd0309ffeea705ed Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Wed, 8 Apr 2020 22:03:03 +0200 Subject: [PATCH 83/93] fixed: missing SetupDiDestroyDeviceInfoList() --- src/libcec/platform/windows/os-edid.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libcec/platform/windows/os-edid.cpp b/src/libcec/platform/windows/os-edid.cpp index 49d73c8d..7bed9e15 100644 --- a/src/libcec/platform/windows/os-edid.cpp +++ b/src/libcec/platform/windows/os-edid.cpp @@ -92,6 +92,7 @@ uint16_t CEDIDParser::GetPhysicalAddress(void) iPA = GetPhysicalAddressFromDevice(hDevHandle, &deviceInfoData); } } + SetupDiDestroyDeviceInfoList(hDevHandle); return iPA; } From 05a3476634b7bf0b98be802f6d15826a7218bc45 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Thu, 16 Apr 2020 23:01:47 +0200 Subject: [PATCH 84/93] changed: adapter detection on windows without advapi --- .../Pulse-Eight/USBCECAdapterDetection.cpp | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp index a97e48dc..3cb49dd0 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp @@ -44,7 +44,6 @@ #include #include #elif defined(__WINDOWS__) -#pragma comment(lib, "advapi32.lib") #pragma comment(lib, "setupapi.lib") #pragma comment(lib, "cfgmgr32.lib") #include @@ -144,38 +143,34 @@ bool CUSBCECAdapterDetection::CanAutodetect(void) } #if defined(__WINDOWS__) +static DEVPROPKEY ADAPTER_LOCATION = { 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14 }; + static bool GetComPortFromDevNode(DEVINST hDevInst, char* strPortName, unsigned int iSize) { - bool bReturn(false); - TCHAR strRegPortName[256]; - strRegPortName[0] = _T('\0'); - DWORD dwSize = sizeof(strRegPortName); - DWORD dwType = 0; - HKEY hDeviceKey; - - // open the device node key - if (CM_Open_DevNode_Key(hDevInst, KEY_QUERY_VALUE, 0, RegDisposition_OpenExisting, &hDeviceKey, CM_REGISTRY_HARDWARE) != CR_SUCCESS) + WCHAR friendlyName[256]; + WCHAR* portLocation; + DEVPROPTYPE PropertyType; + ULONG PropertySize; + + // grab the com port from the device's friendly name + PropertySize = sizeof(friendlyName); + CM_Get_DevNode_PropertyW(hDevInst, &ADAPTER_LOCATION, &PropertyType, (PBYTE)friendlyName, &PropertySize, 0); + if (!!(portLocation = wcsstr(friendlyName, L"COM"))) { - printf("reg key not found\n"); - return bReturn; - } - - // locate the PortName entry. TODO this one doesn't seem to be available in universal - if ((RegQueryValueEx(hDeviceKey, _T("PortName"), NULL, &dwType, reinterpret_cast(strRegPortName), &dwSize) == ERROR_SUCCESS) && - (dwType == REG_SZ) && - _tcslen(strRegPortName) > 3 && - _tcsnicmp(strRegPortName, _T("COM"), 3) == 0 && - _ttoi(&(strRegPortName[3])) > 0) - { - // return the port name - snprintf(strPortName, iSize, "%s", strRegPortName); - bReturn = true; + std::string port; + char narrow[6]; + size_t end; + snprintf(narrow, 6, "%ws", portLocation); + port = std::string(narrow); + if ((end = port.find(")")) != std::string::npos) + { + port = port.substr(0, end); + strncpy(strPortName, port.c_str(), iSize); + return true; + } } - // TODO this one doesn't seem to be available in universal - RegCloseKey(hDeviceKey); - - return bReturn; + return false; } static bool GetPidVidFromDeviceName(const std::string strDevName, int* vid, int* pid) From 8b1094e0012bd5d58920e07a831c1e3934727cc2 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Fri, 24 Apr 2020 22:31:40 +0200 Subject: [PATCH 85/93] changed: don't include v5 features if the version isn't set to 5+ (compat) --- include/cec.h | 2 ++ include/cectypes.h | 10 +++++++++- src/cec-client/cec-client.cpp | 11 +++++++++-- src/libcec/CECClient.cpp | 5 ++++- src/libcec/CECProcessor.cpp | 2 ++ src/libcec/LibCEC.cpp | 2 ++ src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h | 2 ++ src/libcec/adapter/AdapterCommunication.h | 2 ++ .../adapter/Exynos/ExynosCECAdapterCommunication.h | 2 ++ src/libcec/adapter/IMX/IMXCECAdapterCommunication.h | 2 ++ .../adapter/Linux/LinuxCECAdapterCommunication.h | 2 ++ .../adapter/Pulse-Eight/USBCECAdapterCommands.cpp | 4 ++++ .../Pulse-Eight/USBCECAdapterCommunication.cpp | 2 ++ .../adapter/Pulse-Eight/USBCECAdapterCommunication.h | 2 ++ src/libcec/adapter/RPi/RPiCECAdapterCommunication.h | 2 ++ .../adapter/TDA995x/TDA995xCECAdapterCommunication.h | 2 ++ 16 files changed, 50 insertions(+), 4 deletions(-) diff --git a/include/cec.h b/include/cec.h index 3fbeb1c1..607d05a0 100644 --- a/include/cec.h +++ b/include/cec.h @@ -454,7 +454,9 @@ namespace CEC */ virtual bool AudioEnable(bool enable) = 0; +#if CEC_LIB_VERSION_MAJOR >= 5 virtual bool GetStats(struct cec_adapter_stats* stats) = 0; +#endif }; }; diff --git a/include/cectypes.h b/include/cectypes.h index 0b27663b..c97d8ba9 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -1502,7 +1502,9 @@ struct libcec_configuration uint32_t iButtonReleaseDelayMs;/*!< duration after last update until a button is considered released */ uint32_t iDoubleTapTimeoutMs; /*!< prevent double taps within this timeout. defaults to 200ms. added in 4.0.0 */ uint8_t bAutoWakeAVR; /*!< set to 1 to automatically waking an AVR when the source is activated. added in 4.0.0 */ +#if CEC_LIB_VERSION_MAJOR >= 5 uint8_t bAutoPowerOn; /*!< set to 1 and save eeprom config to wake the tv when usb is powered. added in 5.0.0 / fw v9 */ +#endif #ifdef __cplusplus libcec_configuration(void) { Clear(); } @@ -1536,7 +1538,11 @@ struct libcec_configuration iButtonReleaseDelayMs == other.iButtonReleaseDelayMs && comboKey == other.comboKey && iComboKeyTimeoutMs == other.iComboKeyTimeoutMs && - bAutoWakeAVR == other.bAutoWakeAVR); + bAutoWakeAVR == other.bAutoWakeAVR +#if CEC_LIB_VERSION_MAJOR >= 5 + && bAutoPowerOn == other.bAutoPowerOn +#endif + ); } bool operator!=(const libcec_configuration &other) const @@ -1571,7 +1577,9 @@ struct libcec_configuration iButtonRepeatRateMs = 0; iButtonReleaseDelayMs = CEC_BUTTON_TIMEOUT; bAutoWakeAVR = 0; +#if CEC_LIB_VERSION_MAJOR >= 5 bAutoPowerOn = 0; +#endif memset(strDeviceName, 0, 13); deviceTypes.Clear(); diff --git a/src/cec-client/cec-client.cpp b/src/cec-client/cec-client.cpp index 51b7f177..c0755f88 100644 --- a/src/cec-client/cec-client.cpp +++ b/src/cec-client/cec-client.cpp @@ -921,6 +921,7 @@ bool ProcessCommandSCAN(ICECAdapter *parser, const std::string &command, std::st return false; } +#if CEC_LIB_VERSION_MAJOR >= 5 bool ProcessCommandSTATS(ICECAdapter *parser, const std::string &command, std::string & UNUSED(arguments)) { if (command == "stats") @@ -944,6 +945,7 @@ bool ProcessCommandSTATS(ICECAdapter *parser, const std::string &command, std::s } return false; } +#endif bool ProcessConsoleCommand(ICECAdapter *parser, std::string &input) { @@ -985,8 +987,11 @@ bool ProcessConsoleCommand(ICECAdapter *parser, std::string &input) ProcessCommandSCAN(parser, command, input) || ProcessCommandSP(parser, command, input) || ProcessCommandSPL(parser, command, input) || - ProcessCommandSELF(parser, command, input) || - ProcessCommandSTATS(parser, command, input); + ProcessCommandSELF(parser, command, input) +#if CEC_LIB_VERSION_MAJOR >= 5 + || ProcessCommandSTATS(parser, command, input) +#endif + ; } } return true; @@ -1215,6 +1220,7 @@ bool ProcessCommandLineArguments(int argc, char *argv[]) } } #endif +#if CEC_LIB_VERSION_MAJOR >= 5 else if (!strcmp(argv[iArgPtr], "-aw") || !strcmp(argv[iArgPtr], "--autowake")) { @@ -1235,6 +1241,7 @@ bool ProcessCommandLineArguments(int argc, char *argv[]) } ++iArgPtr; } +#endif else { g_strPort = argv[iArgPtr++]; diff --git a/src/libcec/CECClient.cpp b/src/libcec/CECClient.cpp index e78162df..edbeb2c1 100644 --- a/src/libcec/CECClient.cpp +++ b/src/libcec/CECClient.cpp @@ -914,7 +914,9 @@ bool CCECClient::SetConfiguration(const libcec_configuration &configuration) m_configuration.iButtonRepeatRateMs = configuration.iButtonRepeatRateMs; m_configuration.iButtonReleaseDelayMs = configuration.iButtonReleaseDelayMs; m_configuration.bAutoWakeAVR = configuration.bAutoWakeAVR; +#if CEC_LIB_VERSION_MAJOR >= 5 m_configuration.bAutoPowerOn = configuration.bAutoPowerOn; +#endif } bool bNeedReinit(false); @@ -1708,10 +1710,11 @@ bool CCECClient::AudioEnable(bool enable) false; } +#if CEC_LIB_VERSION_MAJOR >= 5 bool CCECClient::GetStats(struct cec_adapter_stats* stats) { return !!m_processor ? m_processor->GetStats(stats) : false; } - +#endif \ No newline at end of file diff --git a/src/libcec/CECProcessor.cpp b/src/libcec/CECProcessor.cpp index fc909d83..9e7734e7 100644 --- a/src/libcec/CECProcessor.cpp +++ b/src/libcec/CECProcessor.cpp @@ -333,12 +333,14 @@ bool CCECProcessor::ActivateSource(uint16_t iStreamPath) return bReturn; } +#if CEC_LIB_VERSION_MAJOR >= 5 bool CCECProcessor::GetStats(struct cec_adapter_stats* stats) { return !!m_communication ? m_communication->GetStats(stats) : false; } +#endif void CCECProcessor::SetActiveSource(bool bSetTo, bool bClientUnregistered) { diff --git a/src/libcec/LibCEC.cpp b/src/libcec/LibCEC.cpp index 70ea733f..8d5e97e7 100644 --- a/src/libcec/LibCEC.cpp +++ b/src/libcec/LibCEC.cpp @@ -625,9 +625,11 @@ bool CLibCEC::AudioEnable(bool enable) false; } +#if CEC_LIB_VERSION_MAJOR >= 5 bool CLibCEC::GetStats(struct cec_adapter_stats* stats) { return !!m_client ? m_client->GetStats(stats) : false; } +#endif \ No newline at end of file diff --git a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h index 2c52c1c5..0843e381 100644 --- a/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h +++ b/src/libcec/adapter/AOCEC/AOCECAdapterCommunication.h @@ -84,7 +84,9 @@ namespace CEC uint16_t GetAdapterProductId(void) const { return 1; } void HandleLogicalAddressLost(cec_logical_address oldAddress); void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} + #if CEC_LIB_VERSION_MAJOR >= 5 bool GetStats(struct cec_adapter_stats* UNUSED(stats)) { return false; } + #endif ///} /** @name P8PLATFORM::CThread implementation */ diff --git a/src/libcec/adapter/AdapterCommunication.h b/src/libcec/adapter/AdapterCommunication.h index 88d12f8b..8af9b227 100644 --- a/src/libcec/adapter/AdapterCommunication.h +++ b/src/libcec/adapter/AdapterCommunication.h @@ -238,7 +238,9 @@ namespace CEC */ virtual void SetActiveSource(bool bSetTo, bool bClientUnregistered) = 0; +#if CEC_LIB_VERSION_MAJOR >= 5 virtual bool GetStats(struct cec_adapter_stats* stats) = 0; +#endif IAdapterCommunicationCallback *m_callback; }; diff --git a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h index b364ea79..8f00c7c0 100644 --- a/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h +++ b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.h @@ -82,7 +82,9 @@ namespace CEC uint16_t GetAdapterProductId(void) const { return 1; } void HandleLogicalAddressLost(cec_logical_address oldAddress); void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} + #if CEC_LIB_VERSION_MAJOR >= 5 bool GetStats(struct cec_adapter_stats* UNUSED(stats)) { return false; } + #endif ///} /** @name P8PLATFORM::CThread implementation */ diff --git a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h index 43d38032..fdf21d4b 100644 --- a/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h +++ b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.h @@ -86,7 +86,9 @@ namespace CEC uint16_t GetAdapterProductId(void) const { return IMX_ADAPTER_PID; } void HandleLogicalAddressLost(cec_logical_address UNUSED(oldAddress)); void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} +#if CEC_LIB_VERSION_MAJOR >= 5 bool GetStats(struct cec_adapter_stats* UNUSED(stats)) { return false; } +#endif ///} /** @name P8PLATFORM::CThread implementation */ diff --git a/src/libcec/adapter/Linux/LinuxCECAdapterCommunication.h b/src/libcec/adapter/Linux/LinuxCECAdapterCommunication.h index 446ba6cc..f3519d40 100644 --- a/src/libcec/adapter/Linux/LinuxCECAdapterCommunication.h +++ b/src/libcec/adapter/Linux/LinuxCECAdapterCommunication.h @@ -80,7 +80,9 @@ namespace CEC uint16_t GetAdapterVendorId(void) const override { return 1; } uint16_t GetAdapterProductId(void) const override { return 1; } void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) override {} +#if CEC_LIB_VERSION_MAJOR >= 5 bool GetStats(struct cec_adapter_stats* UNUSED(stats)) override { return false; } +#endif ///} /** @name P8PLATFORM::CThread implementation */ diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp index 0158cf3f..a2a6ad67 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp @@ -553,7 +553,11 @@ bool CUSBCECAdapterCommands::PersistConfiguration(const libcec_configuration &co bReturn |= SetSettingPhysicalAddress(configuration.iPhysicalAddress); bReturn |= SetSettingOSDName(configuration.strDeviceName); if (m_persistedConfiguration.iFirmwareVersion >= 10) +#if CEC_LIB_VERSION_MAJOR >= 5 bReturn |= SetSettingAutoPowerOn(configuration.bAutoPowerOn); +#else + bReturn |= SetSettingAutoPowerOn(false); +#endif else bReturn |= SetSettingCECVersion(configuration.cecVersion); diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp index 2ec8f094..80a1e41e 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp @@ -635,12 +635,14 @@ void CUSBCECAdapterCommunication::SetActiveSource(bool bSetTo, bool bClientUnreg m_commands->SetActiveSource(bSetTo, bClientUnregistered); } +#if CEC_LIB_VERSION_MAJOR >= 5 bool CUSBCECAdapterCommunication::GetStats(struct cec_adapter_stats* stats) { CLockObject lock(m_statsMutex); memcpy(stats, &m_stats, sizeof(struct cec_adapter_stats)); return true; } +#endif bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void) { diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h index b9d2ada5..b8d2459e 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.h @@ -94,7 +94,9 @@ namespace CEC uint16_t GetAdapterVendorId(void) const; uint16_t GetAdapterProductId(void) const; void SetActiveSource(bool bSetTo, bool bClientUnregistered); +#if CEC_LIB_VERSION_MAJOR >= 5 bool GetStats(struct cec_adapter_stats* stats); +#endif ///} bool ProvidesExtendedResponse(void); diff --git a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h index 90027e30..4c2a4eba 100644 --- a/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h +++ b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.h @@ -88,7 +88,9 @@ namespace CEC uint16_t GetAdapterVendorId(void) const { return RPI_ADAPTER_VID; } uint16_t GetAdapterProductId(void) const { return RPI_ADAPTER_PID; } void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} + #if CEC_LIB_VERSION_MAJOR >= 5 bool GetStats(struct cec_adapter_stats* UNUSED(stats)) { return false; } + #endif ///} bool IsInitialised(void); diff --git a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h index 0cb8e23d..f3cfb760 100644 --- a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h +++ b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h @@ -93,7 +93,9 @@ namespace CEC uint16_t GetAdapterProductId(void) const { return TDA995X_ADAPTER_PID; } void HandleLogicalAddressLost(cec_logical_address oldAddress); void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} + #if CEC_LIB_VERSION_MAJOR >= 5 bool GetStats(struct cec_adapter_stats* UNUSED(stats)) { return false; } + #endif ///} /** @name P8PLATFORM::CThread implementation */ From d5e5200ed1a998af24fa5290ad764e0a2ed086c6 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Mon, 30 Mar 2020 11:18:46 +0200 Subject: [PATCH 86/93] changed/added: .net core support, various build and packaging fixes --- .gitignore | 9 +- CMakeLists.txt | 22 +- COPYING | 2 +- docs/README.developers.md | 12 +- docs/README.windows.md | 50 +- project/libCEC-version.nsh.in | 2 - project/libCEC.nsi | 373 ++------- project/libcec.sln | 19 +- project/nsis/functions.nsh | 42 + project/nsis/libcec-pdb.nsh | 21 + project/nsis/libcec-version.nsh.in | 1 + project/nsis/sections.nsh | 314 +++++++ project/nsis/uninstall.nsh | 95 +++ src/cec-client/CMakeLists.txt | 4 +- src/cecc-client/CMakeLists.txt | 4 +- src/dotnet | 2 +- .../CecSharpTypes.h | 0 src/dotnetlib/LibCecSharp/.gitignore | 6 + .../LibCecSharp/AssemblyInfo.cpp.in | 3 +- .../LibCecSharp/LibCecSharp.cpp | 0 .../LibCecSharp/project}/LibCecSharp.rc.in | 2 +- .../LibCecSharp/project}/LibCecSharp.vcxproj | 50 +- .../project}/LibCecSharp.vcxproj.filters | 14 +- .../dotnetlib/LibCecSharp/project}/resource.h | Bin src/dotnetlib/LibCecSharpCore/.gitignore | 6 + .../LibCecSharpCore/AssemblyInfo.cpp.in | 22 + .../LibCecSharpCore/LibCecSharpCore.cpp | 783 ++++++++++++++++++ .../LibCecSharpCore/LibCecSharpCore.h | 10 + .../project/LibCecSharpCore.vcxproj | 169 ++++ .../project/LibCecSharpCore.vcxproj.filters | 58 ++ .../LibCecSharpCore/project/Resource.h | 3 + src/dotnetlib/LibCecSharpCore/project/app.ico | Bin 0 -> 370070 bytes src/dotnetlib/LibCecSharpCore/project/pch.cpp | 5 + src/dotnetlib/LibCecSharpCore/project/pch.h | 12 + src/libcec/CMakeLists.txt | 51 +- src/libcec/cmake/LinkPlatformSupport.cmake | 25 +- src/platform | 2 +- support | 2 +- windows/build-all.cmd | 128 +++ windows/build-lib.cmd | 49 +- windows/build.cmd | 39 - windows/create-installer.cmd | 224 ++--- windows/nsis-helper.cmd | 56 ++ windows/visual-studio.cmd | 34 +- 44 files changed, 2111 insertions(+), 614 deletions(-) delete mode 100644 project/libCEC-version.nsh.in create mode 100644 project/nsis/functions.nsh create mode 100644 project/nsis/libcec-pdb.nsh create mode 100644 project/nsis/libcec-version.nsh.in create mode 100644 project/nsis/sections.nsh create mode 100644 project/nsis/uninstall.nsh rename src/{LibCecSharp => dotnetlib}/CecSharpTypes.h (100%) create mode 100644 src/dotnetlib/LibCecSharp/.gitignore rename src/{ => dotnetlib}/LibCecSharp/AssemblyInfo.cpp.in (88%) rename src/{ => dotnetlib}/LibCecSharp/LibCecSharp.cpp (100%) rename {project/LibCecSharp => src/dotnetlib/LibCecSharp/project}/LibCecSharp.rc.in (98%) rename {project/LibCecSharp => src/dotnetlib/LibCecSharp/project}/LibCecSharp.vcxproj (80%) rename {project/LibCecSharp => src/dotnetlib/LibCecSharp/project}/LibCecSharp.vcxproj.filters (75%) rename {project/LibCecSharp => src/dotnetlib/LibCecSharp/project}/resource.h (100%) create mode 100644 src/dotnetlib/LibCecSharpCore/.gitignore create mode 100644 src/dotnetlib/LibCecSharpCore/AssemblyInfo.cpp.in create mode 100644 src/dotnetlib/LibCecSharpCore/LibCecSharpCore.cpp create mode 100644 src/dotnetlib/LibCecSharpCore/LibCecSharpCore.h create mode 100644 src/dotnetlib/LibCecSharpCore/project/LibCecSharpCore.vcxproj create mode 100644 src/dotnetlib/LibCecSharpCore/project/LibCecSharpCore.vcxproj.filters create mode 100644 src/dotnetlib/LibCecSharpCore/project/Resource.h create mode 100644 src/dotnetlib/LibCecSharpCore/project/app.ico create mode 100644 src/dotnetlib/LibCecSharpCore/project/pch.cpp create mode 100644 src/dotnetlib/LibCecSharpCore/project/pch.h create mode 100644 windows/build-all.cmd delete mode 100644 windows/build.cmd create mode 100644 windows/nsis-helper.cmd diff --git a/.gitignore b/.gitignore index 269f7647..9fd96d21 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ *.aps env.h version.h +.vs *.rc AssemblyInfo.cs @@ -73,15 +74,13 @@ project/obj project/Properties project/_* project/x64 -project/LibCecSharp/x64 -project/LibCecSharp/Debug -project/LibCecSharp/Release project/libcec/x64 project/libcec/Debug project/libcec/Release project/testclient/x64 project/testclient/Debug project/testclient/Release +project/nsis/libcec-version.nsh project/RPi/toolchain project/RPi/firmware @@ -104,3 +103,7 @@ src/libcec-wmc/obj /dpinst-amd64.exe /documentation + +/src/EventGhost/egplugin_sources/PulseEight/cec +/src/EventGhost/pulse_eight.egplugin + diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fdfc650..3a65fe52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,12 +23,20 @@ add_subdirectory(src/libcec) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/include/version.h) -# resource files for windows +# windows specific files if(WIN32) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/project/LibCecSharp/LibCecSharp.rc.in - ${CMAKE_CURRENT_SOURCE_DIR}/project/LibCecSharp/LibCecSharp.rc) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/LibCecSharp/AssemblyInfo.cpp.in - ${CMAKE_CURRENT_SOURCE_DIR}/src/LibCecSharp/AssemblyInfo.cpp) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/project/libCEC-version.nsh.in - ${CMAKE_CURRENT_SOURCE_DIR}/project/libCEC-version.nsh) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/project/nsis/libcec-version.nsh.in + ${CMAKE_CURRENT_SOURCE_DIR}/project/nsis/libcec-version.nsh) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/dotnetlib/LibCecSharp/project/LibCecSharp.rc.in + ${CMAKE_CURRENT_SOURCE_DIR}/src/dotnetlib/LibCecSharp/project/LibCecSharp.rc) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/dotnetlib/LibCecSharp/AssemblyInfo.cpp.in + ${CMAKE_CURRENT_SOURCE_DIR}/src/dotnetlib/LibCecSharp/AssemblyInfo.cpp) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/dotnetlib/LibCecSharpCore/AssemblyInfo.cpp.in + ${CMAKE_CURRENT_SOURCE_DIR}/src/dotnetlib/LibCecSharpCore/AssemblyInfo.cpp) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/dotnet/src/LibCecTray/LibCECTray.csproj.in + ${CMAKE_CURRENT_SOURCE_DIR}/src/dotnet/src/LibCecTray/LibCECTray.csproj) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/dotnet/src/CecSharpTester/netcore/CecSharpCoreTester.csproj.in + ${CMAKE_CURRENT_SOURCE_DIR}/src/dotnet/src/CecSharpTester/netcore/CecSharpCoreTester.csproj) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/dotnet/src/CecSharpTester/netfx/CecSharpTester.csproj.in + ${CMAKE_CURRENT_SOURCE_DIR}/src/dotnet/src/CecSharpTester/netfx/CecSharpTester.csproj) endif() diff --git a/COPYING b/COPYING index fc14a9bf..abcf81c0 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,6 @@ This file is part of the libCEC(R) library. -libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved. +libCEC(R) is Copyright (C) 2011-2020 Pulse-Eight Limited. All rights reserved. libCEC(R) is a original work, containing original code. libCEC(R) is a trademark of Pulse-Eight Limited. diff --git a/docs/README.developers.md b/docs/README.developers.md index bfbc23c1..94449991 100644 --- a/docs/README.developers.md +++ b/docs/README.developers.md @@ -10,10 +10,16 @@ We provide a C, C++, Python and .NET CLR interface to the adapter. * the API can be found in `include/cecc.h` * an example implementation can be found on https://github.com/Pulse-Eight/libcec/blob/master/src/cecc-client/cecc-client.c -## .NET developers -* add a reference to `LibCecSharp.dll` -* add `cec.dll` to your project and enable "copy to output directory" +## .NET Framework developers +* add a reference to LibCecSharp.dll for the target architecture (x86/amd64). It's installed to `C:\Program Files (x86)\Pulse-Eight\USB-CEC Adapter\netfx` by default +* the minimum .Net Framework version required for LibCecSharp is 4.0 * an example implementation can be found on https://github.com/Pulse-Eight/cec-dotnet/blob/master/src/CecSharpTester/CecSharpClient.cs + +## .NET Core developers +* add a reference to LibCecSharpCore.dll for the target architecture (x86/amd64). It's installed to `C:\Program Files (x86)\Pulse-Eight\USB-CEC Adapter\netcore` by default +* the minimum .Net Core version required for LibCecSharpCore is 3.1 +* an example implementation can be found on https://github.com/Pulse-Eight/cec-dotnet/blob/master/src/CecSharpTester/CecSharpClient.cs + ## Python developers * the API is exported to Python through Swig * an example implementation can be found on https://github.com/Pulse-Eight/libcec/blob/master/src/pyCecClient/pyCecClient.py diff --git a/docs/README.windows.md b/docs/README.windows.md index 8bc1e25c..ed234393 100644 --- a/docs/README.windows.md +++ b/docs/README.windows.md @@ -1,45 +1,37 @@ ## Microsoft Windows -### Developing a .Net application -Building this library is only required if you want to change libCEC or LibCecSharp internally. -To develop a .Net application that uses LibCecSharp: +### Developing a .Net Framework application +To develop a .Net Framework application that uses LibCecSharp: * download the latest binary version from [our website](http://libcec.pulse-eight.com/Downloads) -* add a reference to LibCecSharp.dll for the target architecture (x86/amd64). It's installed to `C:\Program Files (x86)\Pulse-Eight\USB-CEC Adapter` by default -* add cec.dll to your project from the same directory -* right click on cec.dll in the project explorer -* change the copy mode to "copy if newer" +* add a reference to LibCecSharp.dll for the target architecture (x86/amd64). It's installed to `C:\Program Files (x86)\Pulse-Eight\USB-CEC Adapter\netfx` by default +* the minimum .Net Framework version required for LibCecSharp is 4.0 -Example implementations can be found on [Github](https://github.com/Pulse-Eight/cec-dotnet). +An example implementation can be found on [Github](https://github.com/Pulse-Eight/cec-dotnet/tree/master/src/CecSharpTester/netfx/). + +### Developing a .Net Core application +To develop a .Net Core application that uses LibCecSharp: +* download the latest binary version from [our website](http://libcec.pulse-eight.com/Downloads) +* add a reference to LibCecSharpCore.dll for the target architecture (x86/amd64). It's installed to `C:\Program Files (x86)\Pulse-Eight\USB-CEC Adapter\netcore` by default +* the minimum .Net Core version required for LibCecSharpCore is 3.1 + +An example implementation can be found on [Github](https://github.com/Pulse-Eight/cec-dotnet/tree/master/src/CecSharpTester/netcore/). ### Prerequisites To compile libCEC on Windows, you'll need the following dependencies: * [p8-platform](https://github.com/Pulse-Eight/platform) 2.0 or later -* [cmake 2.6 or better](http://www.cmake.org/) -* [Visual Studio 2008 (v90) Professional](https://www.visualstudio.com/) -* [Visual Studio 2010 (v110) (for x64 builds: Professional)](https://www.visualstudio.com/) -* [Visual Studio 2013 (v120) or 2015 (v140)](https://www.visualstudio.com/) +* [cmake 3.12 or newer](http://www.cmake.org/) +* [Visual Studio 2019 (v16) or newer](https://www.visualstudio.com/), with the following options selected: Universal Windows Platform development, Desktop development with C++, .NET Core cross platform development +* [.Net Core 3.1](https://dotnet.microsoft.com/download/dotnet-core/3.1) * To create an installer, you'll need [Nullsoft's NSIS](http://nsis.sourceforge.net/) +* You also need two versions of Python to build an installer: [Python 2.7.13 for x86](https://www.python.org/ftp/python/2.7.13/python-2.7.13.msi), required by the EventGhost plugin and [Python 3.6 or newer for x64](https://www.python.org/) -Visual Studio version must be installed in ascending order, and each version of Visual Studio must be started at least once before the next version is installed. - -The reason for needing all of these versions is that LibCecSharp targets .Net 2.0, to maximise compatibility, but libCEC itself requires C++11. -This means that LibCecSharp requires the 9.0 toolchain and libCEC the 12.0 toolchain. -Because of changes in Visual Studio's build system, Visual Studio 2010 Professional is required to build LibCecSharp in Visual Studio 2013. - -### Installation check - -You can check whether all versions of Visual Studio got installed correctly by checking if the following directories exist: -* Visual Studio 2008: `X:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\msclr` -* Visual Studio 2010: `X:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Platforms\Win32\PlatformToolsets\v90` -* Visual Studio 2010 x64: `X:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Platforms\x64\PlatformToolsets\v90` - -### Visual Studio 2015 -The build scripts have been configured for building with Visual Studio 2013. To use Visual Studio 2015, change `windows\build.cmd`: `SET VSVERSION=12` to `SET VSVERSION=14`. +### Visual Studio +The build scripts have been configured for building with Visual Studio 2019. To use another version Visual Studio, pass the verion number as parameter: `windows\visual-studio.cmd 2019` ### Compilation -To compile libCEC, follow these instructions: +To only compile libCEC, follow these instructions: * `git submodule update --init --recursive` -* run `windows\build.cmd` to build libCEC and LibCecSharp +* run `windows\build-all.cmd` to build libCEC and LibCecSharp To develop for libCEC in Visual Studio: * `git submodule update --init --recursive` diff --git a/project/libCEC-version.nsh.in b/project/libCEC-version.nsh.in deleted file mode 100644 index 40b64e00..00000000 --- a/project/libCEC-version.nsh.in +++ /dev/null @@ -1,2 +0,0 @@ -Name "Pulse-Eight libCEC v${LIBCEC_VERSION_MAJOR}.${LIBCEC_VERSION_MINOR}.${LIBCEC_VERSION_PATCH}" -OutFile "..\build\libCEC-${LIBCEC_VERSION_MAJOR}.${LIBCEC_VERSION_MINOR}.${LIBCEC_VERSION_PATCH}.exe" \ No newline at end of file diff --git a/project/libCEC.nsi b/project/libCEC.nsi index c6f7bd98..75567e8b 100644 --- a/project/libCEC.nsi +++ b/project/libCEC.nsi @@ -1,22 +1,53 @@ ;libCEC installer -;Copyright (C) 2011-2016 Pulse-Eight Ltd. +;Copyright (C) 2011-2020 Pulse-Eight Ltd. ;http://www.pulse-eight.com/ +Var StartMenuFolder +Var VSRedistSetupError +Var VSRedistInstalledX64 +Var VSRedistInstalledX86 +Var EventGhostLocation + !include "MUI2.nsh" !include "nsDialogs.nsh" !include "LogicLib.nsh" !include "x64.nsh" -!include "libCEC-version.nsh" +!include "nsis\libcec-version.nsh" +!include "nsis\functions.nsh" XPStyle on +RequestExecutionLevel admin + +Name "Pulse-Eight libCEC v${LIBCEC_VERSION_STRING}" + + +!ifdef INNER + ; only generate a temporary installer so we can sign the uninstaller if INNER is defined + OutFile "$%TEMP%\libcec_temp_installer.exe" + SetCompress off +!else + ; create the uninstaller first + !makensis '/V1 /DINNER "${__FILE__}"' = 0 + !system 'set __COMPAT_LAYER=RunAsInvoker&"$%TEMP%\libcec_temp_installer.exe"' = 2 + ; sign the uninstaller if the signtool is present + ${!defineifexist} SIGNTOOL_EXISTS ..\support\private\sign-binary.cmd + !ifdef SIGNTOOL_EXISTS + !echo "Signing uninstaller binary" + !system "..\support\private\sign-binary.cmd $%TEMP%\uninstall_libcec.exe" = 0 + !undef SIGNTOOL_EXISTS + !endif + + ; generate a separate installer if pdb files are included, because it more than twice the size + !ifdef NSISINCLUDEPDB + OutFile "..\build\libcec-dbg-${LIBCEC_VERSION_STRING}.exe" + !else + OutFile "..\build\libcec-${LIBCEC_VERSION_STRING}.exe" + !endif + SetCompressor /SOLID lzma +!endif + InstallDir "$PROGRAMFILES\Pulse-Eight\USB-CEC Adapter" InstallDirRegKey HKLM "Software\Pulse-Eight\USB-CEC Adapter software" "" -RequestExecutionLevel admin -Var StartMenuFolder -Var VSRedistSetupError -Var VSRedistInstalledX64 -Var VSRedistInstalledX86 -Var EventGhostLocation !define MUI_FINISHPAGE_LINK "Visit http://libcec.pulse-eight.com/ for more information." !define MUI_FINISHPAGE_LINK_LOCATION "http://libcec.pulse-eight.com/" @@ -46,243 +77,20 @@ InstType "Full installation" InstType "USB-CEC Driver & libCEC" InstType "USB-CEC Driver Only" -Section "USB-CEC Driver" SecDriver - SetShellVarContext current - SectionIn RO - SectionIn 1 2 3 - - ; Renamed to cec.dll - Delete "$INSTDIR\libcec.dll" - ${If} ${RunningX64} - Delete "$INSTDIR\x64\libcec.dll" - ${EndIf} - - ; Copy to the installation directory - SetOutPath "$INSTDIR" - File "..\AUTHORS" - File "..\COPYING" - - ; Copy the driver installer - SetOutPath "$INSTDIR\driver" - File "..\build\p8-usbcec-driver-installer.exe" - - ;Store installation folder - WriteRegStr HKLM "Software\Pulse-Eight\USB-CEC Adapter software" "" $INSTDIR - - ;Create uninstaller - WriteUninstaller "$INSTDIR\Uninstall.exe" - - !insertmacro MUI_STARTMENU_WRITE_BEGIN Application - SetOutPath "$INSTDIR" - - CreateDirectory "$SMPROGRAMS\$StartMenuFolder" - CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall Pulse-Eight USB-CEC Adapter software.lnk" "$INSTDIR\Uninstall.exe" \ - "" "$INSTDIR\Uninstall.exe" 0 SW_SHOWNORMAL \ - "" "Uninstall Pulse-Eight USB-CEC Adapter software." - - WriteINIStr "$SMPROGRAMS\$StartMenuFolder\Visit Pulse-Eight.url" "InternetShortcut" "URL" "http://www.pulse-eight.com/" - !insertmacro MUI_STARTMENU_WRITE_END - - ;add entry to add/remove programs - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ - "DisplayName" "Pulse-Eight USB-CEC Adapter software" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ - "UninstallString" "$INSTDIR\uninstall.exe" - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ - "NoModify" 1 - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ - "NoRepair" 1 - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ - "InstallLocation" "$INSTDIR" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ - "DisplayIcon" "$INSTDIR\cec-client.exe,0" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ - "Publisher" "Pulse-Eight Limited" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ - "HelpLink" "http://www.pulse-eight.com/" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ - "URLInfoAbout" "http://www.pulse-eight.com" - - ;install driver - ExecWait '"$INSTDIR\driver\p8-usbcec-driver-installer.exe" /S' - Delete "$INSTDIR\driver\p8-usbcec-driver-installer.exe" -SectionEnd - -Section "libCEC" SecLibCec - SetShellVarContext current - SectionIn 1 2 - - ; Copy to the installation directory - SetOutPath "$INSTDIR" - File "..\ChangeLog" - File "..\README.md" - File "..\build\x86\*.dll" - File "..\build\x86\*.xml" - SetOutPath "$INSTDIR\x64" - File /nonfatal "..\build\amd64\*.dll" - File /nonfatal "..\build\amd64\*.xml" - - ; Copy to Kodi\system - ReadRegStr $1 HKCU "Software\Kodi" "" - ${If} $1 != "" - SetOutPath "$1\system" - File "..\build\x86\libcec.dll" - ${EndIf} - - ; Copy the headers - SetOutPath "$INSTDIR\include" - File /r /x *.so "..\build\x86\include\libcec\cec*.*" - File /r /x *.so "..\build\x86\include\libcec\version.h" -SectionEnd - -Section "CEC Debug Client" SecCecClient - SetShellVarContext current - SectionIn 1 - - ; Copy to the installation directory - SetOutPath "$INSTDIR" - File "..\build\x86\cec-client.exe" - File "..\build\x86\cecc-client.exe" - SetOutPath "$INSTDIR\x64" - File /nonfatal "..\build\amd64\cec-client.exe" - File /nonfatal "..\build\amd64\cecc-client.exe" - - !insertmacro MUI_STARTMENU_WRITE_BEGIN Application - SetOutPath "$INSTDIR" - - CreateDirectory "$SMPROGRAMS\$StartMenuFolder" - ${If} ${RunningX64} - CreateShortCut "$SMPROGRAMS\$StartMenuFolder\CEC Test client (x64).lnk" "$INSTDIR\x64\cec-client.exe" \ - "" "$INSTDIR\x64\cec-client.exe" 0 SW_SHOWNORMAL \ - "" "Start the CEC Test client (x64)." - ${Else} - CreateShortCut "$SMPROGRAMS\$StartMenuFolder\CEC Test client.lnk" "$INSTDIR\cec-client.exe" \ - "" "$INSTDIR\cec-client.exe" 0 SW_SHOWNORMAL \ - "" "Start the CEC Test client." - ${EndIf} - !insertmacro MUI_STARTMENU_WRITE_END - -SectionEnd - -Section "libCEC Tray" SecDotNet - SetShellVarContext current - SectionIn 1 2 - - ; Copy to the installation directory - SetOutPath "$INSTDIR" - File "..\build\x86\CecSharpTester.exe" - File "..\build\x86\cec-tray.exe" - SetOutPath "$INSTDIR\x64" - File /nonfatal "..\build\amd64\CecSharpTester.exe" - File /nonfatal "..\build\amd64\cec-tray.exe" - - !insertmacro MUI_STARTMENU_WRITE_BEGIN Application - SetOutPath "$INSTDIR" - - CreateDirectory "$SMPROGRAMS\$StartMenuFolder" - ${If} ${RunningX64} - CreateShortCut "$SMPROGRAMS\$StartMenuFolder\cec-tray.lnk" "$INSTDIR\x64\cec-tray.exe" \ - "" "$INSTDIR\x64\cec-tray.exe" 0 SW_SHOWNORMAL \ - "" "Start libCEC Tray (x64)." - ${Else} - CreateShortCut "$SMPROGRAMS\$StartMenuFolder\cec-tray.lnk" "$INSTDIR\cec-tray.exe" \ - "" "$INSTDIR\cec-tray.exe" 0 SW_SHOWNORMAL \ - "" "Start libCEC Tray." - ${EndIf} - !insertmacro MUI_STARTMENU_WRITE_END - -SectionEnd - -Section "Python bindings" SecPythonCec - SetShellVarContext current - SectionIn 1 2 - - ; Copy to the installation directory - SetOutPath "$INSTDIR\python" - File "..\build\x86\python\pyCecClient.py" - SetOutPath "$INSTDIR\python\cec" - File "..\build\x86\python\cec\_cec.pyd" - File "..\build\x86\python\cec\__init__.py" -SectionEnd - -; Function used to get the parent directory of the installer -Function GetParentDirectory - - Exch $R0 - Push $R1 - Push $R2 - Push $R3 - - StrCpy $R1 0 - StrLen $R2 $R0 - - loop: - IntOp $R1 $R1 + 1 - IntCmp $R1 $R2 get 0 get - StrCpy $R3 $R0 1 -$R1 - StrCmp $R3 "\" get - Goto loop - - get: - StrCpy $R0 $R0 -$R1 - - Pop $R3 - Pop $R2 - Pop $R1 - Exch $R0 - -FunctionEnd - -!define EVENTGHOST_SECTIONNAME "EventGhost plugin" -Section "" SecEvGhostCec - SetShellVarContext current - SectionIn 1 2 - - ${If} $EventGhostLocation != "" - ; We get the directory of the installer then pass it to GetParentDirectory - ; which we then append the path to the plugin file to the returned value - ; This is done because EventGhost needs to see the full path to the plugin - ; file. - Push $EXEDIR - Call GetParentDirectory - Pop $R0 - ExecWait '"$EventGhostLocation\eventghost.exe" $R0\src\EventGhost\pulse_eight.egplugin' - ${EndIf} -SectionEnd - -!define REDISTRIBUTABLE_X86_SECTIONNAME "Microsoft Visual C++ 2015 Redistributable Package (x86)" -Section "" SecVCRedistX86 - SetShellVarContext current - SectionIn 1 2 3 - - SetOutPath "$TEMP\vc2015_x86" - - ${If} $VSRedistInstalledX86 != "Yes" - NSISdl::download https://download.microsoft.com/download/6/D/F/6DF3FF94-F7F9-4F0B-838C-A328D1A7D0EE/vc_redist.x86.exe vc_redist.x86.exe - ExecWait '"$TEMP\vc2015_x86\vc_redist.x86.exe" /q' $VSRedistSetupError - ${Endif} - - RMDIR /r "$TEMP\vc2015_x86" -SectionEnd - -!define REDISTRIBUTABLE_X64_SECTIONNAME "Microsoft Visual C++ 2015 Redistributable Package (x64)" -Section "" SecVCRedistX64 - SetShellVarContext current - SectionIn 1 2 3 - - SetOutPath "$TEMP\vc2015_x64" - - ${If} $VSRedistInstalledX64 != "Yes" - NSISdl::download https://download.microsoft.com/download/6/D/F/6DF3FF94-F7F9-4F0B-838C-A328D1A7D0EE/vc_redist.x64.exe vc_redist.x64.exe - ExecWait '"$TEMP\vc2015_x64\vc_redist.x64.exe" /q' $VSRedistSetupError - ${Endif} - - RMDIR /r "$TEMP\vc2015_x64" -SectionEnd +; installer sections, separate file to declutter a bit +!include "nsis\sections.nsh" Function .onInit - ; check for vc2015 x86 redist - ReadRegDword $1 HKLM "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{462f63a8-6347-4894-a1b3-dbfe3a4c981d}" "BundleVersion" +!ifdef INNER + ; just write the uninstaller and exit + SetSilent silent + WriteUninstaller "$%TEMP%\uninstall_libcec.exe" + Quit +!else + ; the actual onInit function + + ; check for vc x86 redist + ReadRegDword $1 HKLM "SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x86" "Version" ${If} $1 != "" StrCpy $VSRedistInstalledX86 "Yes" ${Endif} @@ -296,8 +104,8 @@ Function .onInit ${Endif} ${If} ${RunningX64} - ; check for vc2015 x64 redist - ReadRegDword $1 HKLM "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{323dad84-0974-4d90-a1c1-e006c7fdbb7d}" "BundleVersion" + ; check for vc x64 redist + ReadRegDword $1 HKLM "SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Version" ${If} $1 != "" StrCpy $VSRedistInstalledX64 "Yes" ${Endif} @@ -322,71 +130,20 @@ Function .onInit SectionSetText ${SecEvGhostCec} "${EVENTGHOST_SECTIONNAME}" ${Else} !insertMacro UnSelectSection ${SecEvGhostCec} - SectionSetText ${SecEvGhostCec} "" - MessageBox MB_OK \ - "EventGhost was not found, so the plugin for EventGhost will not be installed. You can download EventGhost from http://www.eventghost.org/" ${Endif} -FunctionEnd - -;-------------------------------- -;Uninstaller Section -Section "Uninstall" - - SetShellVarContext current - - Delete "$INSTDIR\AUTHORS" - Delete "$INSTDIR\*.exe" - Delete "$INSTDIR\ChangeLog" - Delete "$INSTDIR\COPYING" - Delete "$INSTDIR\*.dll" - Delete "$INSTDIR\*.lib" - Delete "$INSTDIR\*.xml" - Delete "$INSTDIR\x64\*.dll" - Delete "$INSTDIR\x64\*.lib" - Delete "$INSTDIR\x64\*.exe" - Delete "$INSTDIR\x64\*.xml" - Delete "$INSTDIR\README.md" - Delete "$SYSDIR\libcec.dll" + ; check for Kodi + IfFileExists "$PROGRAMFILES32\Kodi\cec.dll" 0 +2 + SectionSetText ${SecLibCecKodi86} "${KODI_X86_SECTIONNAME}" ${If} ${RunningX64} - Delete "$SYSDIR\libcec.x64.dll" - ${EndIf} - - ; Uninstall EventGhost plugin - ; Eventghost has no uninstall plugin feature so we sinply delete the plugin - ; from the directory. - ReadRegDword $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\EventGhost_is1" "InstallLocation" - ${If} $1 != "" - RMDir /r "$%PROGRAMDATA%\EventGhost\plugins\PulseEight" + IfFileExists "$PROGRAMFILES64\Kodi\cec.dll" 0 +2 + SectionSetText ${SecLibCecKodi64} "${KODI_X64_SECTIONNAME}" ${Endif} + !insertMacro UnSelectSection ${SecLibCecKodi86} + !insertMacro UnSelectSection ${SecLibCecKodi64} +!endif +FunctionEnd - ; Uninstall the driver - ReadRegStr $1 HKLM "Software\Pulse-Eight\USB-CEC Adapter driver" "" - ${If} $1 != "" - ExecWait '"$1\Uninstall.exe" /S _?=$1' - ${EndIf} - - RMDir /r "$INSTDIR\include" - Delete "$INSTDIR\Uninstall.exe" - RMDir /r "$INSTDIR" - RMDir "$PROGRAMFILES\Pulse-Eight" - - !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder - Delete "$SMPROGRAMS\$StartMenuFolder\libCEC Tray.lnk" - ${If} ${RunningX64} - Delete "$SMPROGRAMS\$StartMenuFolder\libCEC Tray (x64).lnk" - ${EndIf} - Delete "$SMPROGRAMS\$StartMenuFolder\cec-tray.lnk" - Delete "$SMPROGRAMS\$StartMenuFolder\CEC Test client.lnk" - ${If} ${RunningX64} - Delete "$SMPROGRAMS\$StartMenuFolder\CEC Test client (x64).lnk" - ${EndIf} - Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall Pulse-Eight USB-CEC Adapter software.lnk" - Delete "$SMPROGRAMS\$StartMenuFolder\Visit Pulse-Eight.url" - RMDir "$SMPROGRAMS\$StartMenuFolder" - - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter driver" - DeleteRegKey /ifempty HKLM "Software\Pulse-Eight\USB-CEC Adapter software" - DeleteRegKey /ifempty HKLM "Software\Pulse-Eight" -SectionEnd +!ifdef INNER +!include "nsis\uninstall.nsh" +!endif diff --git a/project/libcec.sln b/project/libcec.sln index d51597ab..cc64f743 100644 --- a/project/libcec.sln +++ b/project/libcec.sln @@ -1,12 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibCecSharp", "LibCecSharp\LibCecSharp.vcxproj", "{E54D4581-CD59-4687-BB10-694B8192EABA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibCecSharp", "..\src\dotnetlib\LibCecSharp\project\LibCecSharp.vcxproj", "{E54D4581-CD59-4687-BB10-694B8192EABA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B119505D-5CD1-48E4-BED1-9C2BF26153D5}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibCecSharpCore", "..\src\dotnetlib\LibCecSharpCore\project\LibCecSharpCore.vcxproj", "{E8C30CBD-64D1-44F8-9172-82B728986DCC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -23,8 +25,19 @@ Global {E54D4581-CD59-4687-BB10-694B8192EABA}.Release|x64.Build.0 = Release|x64 {E54D4581-CD59-4687-BB10-694B8192EABA}.Release|x86.ActiveCfg = Release|Win32 {E54D4581-CD59-4687-BB10-694B8192EABA}.Release|x86.Build.0 = Release|Win32 + {E8C30CBD-64D1-44F8-9172-82B728986DCC}.Debug|x64.ActiveCfg = Debug|x64 + {E8C30CBD-64D1-44F8-9172-82B728986DCC}.Debug|x64.Build.0 = Debug|x64 + {E8C30CBD-64D1-44F8-9172-82B728986DCC}.Debug|x86.ActiveCfg = Debug|Win32 + {E8C30CBD-64D1-44F8-9172-82B728986DCC}.Debug|x86.Build.0 = Debug|Win32 + {E8C30CBD-64D1-44F8-9172-82B728986DCC}.Release|x64.ActiveCfg = Release|x64 + {E8C30CBD-64D1-44F8-9172-82B728986DCC}.Release|x64.Build.0 = Release|x64 + {E8C30CBD-64D1-44F8-9172-82B728986DCC}.Release|x86.ActiveCfg = Release|Win32 + {E8C30CBD-64D1-44F8-9172-82B728986DCC}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {79F89A04-C65A-4AC3-A89C-42B3C1F2623A} + EndGlobalSection EndGlobal diff --git a/project/nsis/functions.nsh b/project/nsis/functions.nsh new file mode 100644 index 00000000..718896ca --- /dev/null +++ b/project/nsis/functions.nsh @@ -0,0 +1,42 @@ +!macro !defineifexist _VAR_NAME _FILE_NAME + !tempfile _TEMPFILE + !ifdef NSIS_WIN32_MAKENSIS + ; Windows - cmd.exe + !system 'if exist "${_FILE_NAME}" echo !define ${_VAR_NAME} > "${_TEMPFILE}"' + !else + ; Posix - sh + !system 'if [ -e "${_FILE_NAME}" ]; then echo "!define ${_VAR_NAME}" > "${_TEMPFILE}"; fi' + !endif + !include '${_TEMPFILE}' + !delfile '${_TEMPFILE}' + !undef _TEMPFILE +!macroend +!define !defineifexist "!insertmacro !defineifexist" + +; Function used to get the parent directory of the installer +Function GetParentDirectory + + Exch $R0 + Push $R1 + Push $R2 + Push $R3 + + StrCpy $R1 0 + StrLen $R2 $R0 + + loop: + IntOp $R1 $R1 + 1 + IntCmp $R1 $R2 get 0 get + StrCpy $R3 $R0 1 -$R1 + StrCmp $R3 "\" get + Goto loop + + get: + StrCpy $R0 $R0 -$R1 + + Pop $R3 + Pop $R2 + Pop $R1 + Exch $R0 + +FunctionEnd \ No newline at end of file diff --git a/project/nsis/libcec-pdb.nsh b/project/nsis/libcec-pdb.nsh new file mode 100644 index 00000000..65c5b16a --- /dev/null +++ b/project/nsis/libcec-pdb.nsh @@ -0,0 +1,21 @@ +Section "libCEC debug symbols" SecPDB + SetShellVarContext current + SectionIn 1 + + ; Copy to the installation directory + SetOutPath "$INSTDIR" + File "..\build\x86\lib\cec.pdb" + SetOutPath "$INSTDIR\netfx" + File "..\build\x86\LibCecSharp.pdb" + SetOutPath "$INSTDIR\netcore" + File "..\build\x86\netcore\LibCecSharpCore.pdb" + File "..\build\x86\netcore\CecSharpCoreTester.pdb" + + SetOutPath "$INSTDIR\x64" + File "..\build\amd64\lib\cec.pdb" + SetOutPath "$INSTDIR\x64\netfx" + File "..\build\amd64\LibCecSharp.pdb" + SetOutPath "$INSTDIR\x64\netcore" + File "..\build\amd64\netcore\LibCecSharpCore.pdb" + File "..\build\amd64\netcore\CecSharpCoreTester.pdb" +SectionEnd diff --git a/project/nsis/libcec-version.nsh.in b/project/nsis/libcec-version.nsh.in new file mode 100644 index 00000000..566c965a --- /dev/null +++ b/project/nsis/libcec-version.nsh.in @@ -0,0 +1 @@ +!define LIBCEC_VERSION_STRING "${LIBCEC_VERSION_MAJOR}.${LIBCEC_VERSION_MINOR}.${LIBCEC_VERSION_PATCH}" \ No newline at end of file diff --git a/project/nsis/sections.nsh b/project/nsis/sections.nsh new file mode 100644 index 00000000..63efb783 --- /dev/null +++ b/project/nsis/sections.nsh @@ -0,0 +1,314 @@ +Section "USB-CEC driver" SecDriver + SetShellVarContext current + SectionIn RO + SectionIn 1 2 3 + ; Copy the driver installer + SetOutPath "$INSTDIR\driver" + File "..\build\p8-usbcec-driver-installer.exe" + ;install driver + ExecWait '"$INSTDIR\driver\p8-usbcec-driver-installer.exe" /S' + Delete "$INSTDIR\driver\p8-usbcec-driver-installer.exe" +SectionEnd + +Section "libCEC" SecLibCec + SetShellVarContext current + SectionIn 1 2 + SectionIn RO + + ; Renamed to cec.dll + Delete "$INSTDIR\libcec.dll" + ${If} ${RunningX64} + Delete "$INSTDIR\x64\libcec.dll" + ${EndIf} + + ; Moved to netfx subdir + Delete "$INSTDIR\CecSharpTester.exe" + Delete "$INSTDIR\cec-tray.exe" + Delete "$INSTDIR\LibCecSharp.dll" + Delete "$INSTDIR\LibCecSharp.xml" + ${If} ${RunningX64} + Delete "$INSTDIR\x64\CecSharpTester.exe" + Delete "$INSTDIR\x64\cec-tray.exe" + Delete "$INSTDIR\x64\LibCecSharp.dll" + Delete "$INSTDIR\x64\LibCecSharp.xml" + ${EndIf} + + ; Copy to the installation directory + SetOutPath "$INSTDIR" + File "..\ChangeLog" + File "..\README.md" + File "..\docs\README.developers.md" + File "..\docs\README.windows.md" + File "..\build\x86\cec.dll" + SetOutPath "$INSTDIR\x64" + File /nonfatal "..\build\amd64\cec.dll" + + ; Copy the headers + SetOutPath "$INSTDIR\include" + File /r /x *.so "..\build\x86\include\libcec\*.h" + + ; Copy to the installation directory + SetOutPath "$INSTDIR" + File "..\AUTHORS" + File "..\COPYING" + + ;Store installation folder + WriteRegStr HKLM "Software\Pulse-Eight\USB-CEC Adapter software" "" $INSTDIR + + ;Package uninstaller + !ifndef INNER + SetOutPath $INSTDIR + File $%TEMP%\uninstall_libcec.exe + !endif + + + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + SetOutPath "$INSTDIR" + + CreateDirectory "$SMPROGRAMS\$StartMenuFolder" + CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall Pulse-Eight USB-CEC Adapter software.lnk" "$INSTDIR\uninstall_libcec.exe" \ + "" "$INSTDIR\Uninstall.exe" 0 SW_SHOWNORMAL \ + "" "Uninstall Pulse-Eight USB-CEC Adapter software." + + WriteINIStr "$SMPROGRAMS\$StartMenuFolder\Visit Pulse-Eight.url" "InternetShortcut" "URL" "http://www.pulse-eight.com/" + !insertmacro MUI_STARTMENU_WRITE_END + + ;add entry to add/remove programs + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ + "DisplayName" "Pulse-Eight USB-CEC Adapter software" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ + "UninstallString" "$INSTDIR\uninstall.exe" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ + "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ + "NoRepair" 1 + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ + "InstallLocation" "$INSTDIR" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ + "DisplayIcon" "$INSTDIR\cec-client.exe,0" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ + "Publisher" "Pulse-Eight Limited" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ + "HelpLink" "http://www.pulse-eight.com/" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" \ + "URLInfoAbout" "http://www.pulse-eight.com" +SectionEnd + +Section "libCEC for Python" SecPythonCec + SetShellVarContext current + SectionIn 1 2 + + ; Copy to the installation directory + SetOutPath "$INSTDIR\python" + File "..\build\x86\python\_cec.pyd" + SetOutPath "$INSTDIR\python\cec" + File "..\build\x86\python\cec\cec.py" + File "..\build\x86\python\cec\__init__.py" +SectionEnd + +Section "libCEC for .Net Framework" SecDotNet + SetShellVarContext current + SectionIn 1 2 + + ; Copy to the installation directory + SetOutPath "$INSTDIR\netfx" + File "..\build\x86\LibCecSharp.dll" + File "..\build\x86\LibCecSharp.xml" + File "..\build\x86\CecSharpTester.exe" + SetOutPath "$INSTDIR\x64\netfx" + File /nonfatal "..\build\amd64\CecSharpTester.exe" + File /nonfatal "..\build\amd64\LibCecSharp.dll" + File /nonfatal "..\build\amd64\LibCecSharp.xml" +SectionEnd + +Section "libCEC for .Net Core" SecDotNetCore + SetShellVarContext current + SectionIn 1 2 + + ; Copy to the installation directory + SetOutPath "$INSTDIR\netcore" + File "..\build\x86\netcore\LibCecSharpCore.deps.json" + File "..\build\x86\netcore\LibCecSharpCore.dll" + File "..\build\x86\netcore\LibCecSharpCore.runtimeconfig.json" + File "..\build\x86\netcore\LibCecSharpCore.xml" + File "..\build\x86\netcore\CecSharpCoreTester.exe" + File "..\build\x86\netcore\CecSharpCoreTester.deps.json" + File "..\build\x86\netcore\CecSharpCoreTester.dll" + File "..\build\x86\netcore\CecSharpCoreTester.runtimeconfig.json" + File "..\build\x86\netcore\Ijwhost.dll" + SetOutPath "$INSTDIR\x64\netcore" + File /nonfatal "..\build\amd64\netcore\LibCecSharpCore.deps.json" + File /nonfatal "..\build\amd64\netcore\LibCecSharpCore.dll" + File /nonfatal "..\build\amd64\netcore\LibCecSharpCore.runtimeconfig.json" + File /nonfatal "..\build\amd64\netcore\LibCecSharpCore.xml" + File /nonfatal "..\build\amd64\netcore\CecSharpCoreTester.exe" + File /nonfatal "..\build\amd64\netcore\CecSharpCoreTester.deps.json" + File /nonfatal "..\build\amd64\netcore\CecSharpCoreTester.dll" + File /nonfatal "..\build\amd64\netcore\CecSharpCoreTester.runtimeconfig.json" + File /nonfatal "..\build\amd64\netcore\Ijwhost.dll" +SectionEnd + +Section "libCEC Tray" SecTray + SetShellVarContext current + SectionIn 1 + + ; Copy to the installation directory + SetOutPath "$INSTDIR\netfx" + File "..\build\x86\cec-tray.exe" + SetOutPath "$INSTDIR\x64\netfx" + File /nonfatal "..\build\amd64\cec-tray.exe" + + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + SetOutPath "$INSTDIR" + + CreateDirectory "$SMPROGRAMS\$StartMenuFolder" + ${If} ${RunningX64} + CreateShortCut "$SMPROGRAMS\$StartMenuFolder\cec-tray.lnk" "$INSTDIR\x64\netfx\cec-tray.exe" \ + "" "$INSTDIR\x64\netfx\cec-tray.exe" 0 SW_SHOWNORMAL \ + "" "Start libCEC Tray (x64)." + ${Else} + CreateShortCut "$SMPROGRAMS\$StartMenuFolder\cec-tray.lnk" "$INSTDIR\netfx\cec-tray.exe" \ + "" "$INSTDIR\netfx\cec-tray.exe" 0 SW_SHOWNORMAL \ + "" "Start libCEC Tray." + ${EndIf} + !insertmacro MUI_STARTMENU_WRITE_END +SectionEnd + +Section "libCEC client (cec-client)" SecCecClient + SetShellVarContext current + SectionIn 1 + + ; Copy to the installation directory + SetOutPath "$INSTDIR" + File "..\build\x86\cec-client.exe" + File "..\build\x86\cecc-client.exe" + SetOutPath "$INSTDIR\x64" + File /nonfatal "..\build\amd64\cec-client.exe" + File /nonfatal "..\build\amd64\cecc-client.exe" + + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + SetOutPath "$INSTDIR" + + CreateDirectory "$SMPROGRAMS\$StartMenuFolder" + ${If} ${RunningX64} + CreateShortCut "$SMPROGRAMS\$StartMenuFolder\CEC Test client (x64).lnk" "$INSTDIR\x64\cec-client.exe" \ + "" "$INSTDIR\x64\cec-client.exe" 0 SW_SHOWNORMAL \ + "" "Start the CEC Test client (x64)." + ${Else} + CreateShortCut "$SMPROGRAMS\$StartMenuFolder\CEC Test client.lnk" "$INSTDIR\cec-client.exe" \ + "" "$INSTDIR\cec-client.exe" 0 SW_SHOWNORMAL \ + "" "Start the CEC Test client." + ${EndIf} + !insertmacro MUI_STARTMENU_WRITE_END + +SectionEnd + +Section "Python client" SecPythonCecClient + SetShellVarContext current + SectionIn 1 + + SetOutPath "$INSTDIR\python" + File "..\build\x86\python\pyCecClient.py" +SectionEnd + +!define KODI_X86_SECTIONNAME "Kodi integration (x86)" +Section "" SecLibCecKodi86 + SetShellVarContext current + SectionIn 1 + + SetOutPath "$PROGRAMFILES32\Kodi" + File "..\build\x86\cec.dll" +SectionEnd + +!define KODI_X64_SECTIONNAME "Kodi integration (x64)" +Section "" SecLibCecKodi64 + SetShellVarContext current + SectionIn 1 + + SetOutPath "$PROGRAMFILES64\Kodi" + File "..\build\amd64\cec.dll" +SectionEnd + +!define EVENTGHOST_SECTIONNAME "EventGhost plugin" +Section "" SecEvGhostCec + SetShellVarContext current + SectionIn 1 + + ${If} $EventGhostLocation != "" + ; We get the directory of the installer then pass it to GetParentDirectory + ; which we then append the path to the plugin file to the returned value + ; This is done because EventGhost needs to see the full path to the plugin + ; file. + Push $EXEDIR + Call GetParentDirectory + Pop $R0 + ExecWait '"$EventGhostLocation\eventghost.exe" $R0\src\EventGhost\pulse_eight.egplugin' + ${EndIf} +SectionEnd + +!ifdef NSISINCLUDEPDB +!include "nsis\libcec-pdb.nsh" +!endif + +!define REDISTRIBUTABLE_X86_SECTIONNAME "Microsoft Visual C++ Redistributable Package (x86)" +Section "" SecVCRedistX86 + SetShellVarContext current + SectionIn 1 2 3 + SectionIn RO + + SetOutPath "$TEMP\vc_x86" + + ${If} $VSRedistInstalledX86 != "Yes" + NSISdl::download https://aka.ms/vs/16/release/vc_redist.x86.exe vc_redist.x86.exe + ExecWait '"$TEMP\vc_x86\vc_redist.x86.exe" /q' $VSRedistSetupError + ${Endif} + + RMDIR /r "$TEMP\vc_x86" +SectionEnd + +!define REDISTRIBUTABLE_X64_SECTIONNAME "Microsoft Visual C++ Redistributable Package (x64)" +Section "" SecVCRedistX64 + SetShellVarContext current + SectionIn 1 2 3 + SectionIn RO + + SetOutPath "$TEMP\vc_x64" + + ${If} $VSRedistInstalledX64 != "Yes" + NSISdl::download https://aka.ms/vs/16/release/vc_redist.x64.exe vc_redist.x64.exe + ExecWait '"$TEMP\vc_x64\vc_redist.x64.exe" /q' $VSRedistSetupError + ${Endif} + + RMDIR /r "$TEMP\vc_x64" +SectionEnd + +; Required options +Function .onSelChange +${If} ${SectionIsSelected} ${SecTray} + !define /math MYSECTIONFLAGS ${SF_SELECTED} | ${SF_RO} + !insertmacro SetSectionFlag ${SecDotNet} ${MYSECTIONFLAGS} + !undef MYSECTIONFLAGS +${Else} + !insertmacro ClearSectionFlag ${SecDotNet} ${SF_RO} +${EndIf} + +${If} ${SectionIsSelected} ${SecPythonCecClient} + !define /math MYSECTIONFLAGS ${SF_SELECTED} | ${SF_RO} + !insertmacro SetSectionFlag ${SecPythonCec} ${MYSECTIONFLAGS} + !undef MYSECTIONFLAGS +${Else} + !insertmacro ClearSectionFlag ${SecPythonCec} ${SF_RO} +${EndIf} + +${If} ${SectionIsSelected} ${SecCecClient} +${OrIf} ${SectionIsSelected} ${SecDotNet} +${OrIf} ${SectionIsSelected} ${SecDotNetCore} +${OrIf} ${SectionIsSelected} ${SecPythonCec} + !define /math MYSECTIONFLAGS ${SF_SELECTED} | ${SF_RO} + !insertmacro SetSectionFlag ${SecLibCec} ${MYSECTIONFLAGS} + !undef MYSECTIONFLAGS +${Else} + !insertmacro ClearSectionFlag ${SecLibCec} ${SF_RO} +${EndIf} + +FunctionEnd diff --git a/project/nsis/uninstall.nsh b/project/nsis/uninstall.nsh new file mode 100644 index 00000000..5e74edc2 --- /dev/null +++ b/project/nsis/uninstall.nsh @@ -0,0 +1,95 @@ +;Uninstaller Section +Section "Uninstall" + + SetShellVarContext current + + Delete "$INSTDIR\AUTHORS" + Delete "$INSTDIR\ChangeLog" + Delete "$INSTDIR\COPYING" + Delete "$INSTDIR\README.md" + Delete "$INSTDIR\README.developers.md" + Delete "$INSTDIR\README.windows.md" + Delete "$INSTDIR\cec.dll" + Delete "$INSTDIR\cec.pdb" + Delete "$INSTDIR\cecc-client.exe" + Delete "$INSTDIR\cec-client.exe" + Delete "$INSTDIR\include\cec*.h" + Delete "$INSTDIR\netcore\LibCecSharpCore.deps.json" + Delete "$INSTDIR\netcore\LibCecSharpCore.dll" + Delete "$INSTDIR\netcore\LibCecSharpCore.pdb" + Delete "$INSTDIR\netcore\LibCecSharpCore.runtimeconfig.json" + Delete "$INSTDIR\netcore\LibCecSharpCore.xml" + Delete "$INSTDIR\netcore\CecSharpCoreTester.deps.json" + Delete "$INSTDIR\netcore\CecSharpCoreTester.dll" + Delete "$INSTDIR\netcore\CecSharpCoreTester.runtimeconfig.json" + Delete "$INSTDIR\netcore\CecSharpCoreTester.exe" + Delete "$INSTDIR\netcore\CecSharpCoreTester.pdb" + Delete "$INSTDIR\netfx\LibCecSharp.dll" + Delete "$INSTDIR\netfx\LibCecSharp.pdb" + Delete "$INSTDIR\netfx\LibCecSharp.xml" + Delete "$INSTDIR\netfx\cec-tray.exe" + Delete "$INSTDIR\netfx\CecSharpTester.exe" + Delete "$INSTDIR\python\_cec.pyd" + Delete "$INSTDIR\python\cec\cec.py" + Delete "$INSTDIR\python\cec\__init__.py" + Delete "$INSTDIR\python\pyCecClient.py" + ${If} ${RunningX64} + Delete "$INSTDIR\x64\cec.dll" + Delete "$INSTDIR\x64\cec.pdb" + Delete "$INSTDIR\x64\cecc-client.exe" + Delete "$INSTDIR\x64\cec-client.exe" + Delete "$INSTDIR\x64\netcore\LibCecSharpCore.deps.json" + Delete "$INSTDIR\x64\netcore\LibCecSharpCore.dll" + Delete "$INSTDIR\x64\netcore\LibCecSharpCore.pdb" + Delete "$INSTDIR\x64\netcore\LibCecSharpCore.runtimeconfig.json" + Delete "$INSTDIR\x64\netcore\LibCecSharpCore.xml" + Delete "$INSTDIR\x64\netcore\CecSharpCoreTester.deps.json" + Delete "$INSTDIR\x64\netcore\CecSharpCoreTester.dll" + Delete "$INSTDIR\x64\netcore\CecSharpCoreTester.runtimeconfig.json" + Delete "$INSTDIR\x64\netcore\CecSharpCoreTester.exe" + Delete "$INSTDIR\x64\netcore\CecSharpCoreTester.pdb" + Delete "$INSTDIR\x64\netfx\LibCecSharp.dll" + Delete "$INSTDIR\x64\netfx\LibCecSharp.pdb" + Delete "$INSTDIR\x64\netfx\LibCecSharp.xml" + Delete "$INSTDIR\x64\netfx\cec-tray.exe" + Delete "$INSTDIR\x64\netfx\CecSharpTester.exe" + ${EndIf} + + ; Uninstall EventGhost plugin + ; Eventghost has no uninstall plugin feature so we sinply delete the plugin + ; from the directory. + ReadRegDword $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\EventGhost_is1" "InstallLocation" + ${If} $1 != "" + RMDir /r "$%PROGRAMDATA%\EventGhost\plugins\PulseEight" + ${Endif} + + ; Uninstall the driver + ReadRegStr $1 HKLM "Software\Pulse-Eight\USB-CEC Adapter driver" "" + ${If} $1 != "" + ExecWait '"$1\Uninstall.exe" /S _?=$1' + ${EndIf} + + RMDir /r "$INSTDIR\include" + Delete "$INSTDIR\uninstall_libcec.exe" + RMDir /r "$INSTDIR" + RMDir "$PROGRAMFILES\Pulse-Eight" + + !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder + Delete "$SMPROGRAMS\$StartMenuFolder\libCEC Tray.lnk" + ${If} ${RunningX64} + Delete "$SMPROGRAMS\$StartMenuFolder\libCEC Tray (x64).lnk" + ${EndIf} + Delete "$SMPROGRAMS\$StartMenuFolder\cec-tray.lnk" + Delete "$SMPROGRAMS\$StartMenuFolder\CEC Test client.lnk" + ${If} ${RunningX64} + Delete "$SMPROGRAMS\$StartMenuFolder\CEC Test client (x64).lnk" + ${EndIf} + Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall Pulse-Eight USB-CEC Adapter software.lnk" + Delete "$SMPROGRAMS\$StartMenuFolder\Visit Pulse-Eight.url" + RMDir "$SMPROGRAMS\$StartMenuFolder" + + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter sofware" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pulse-Eight USB-CEC Adapter driver" + DeleteRegKey /ifempty HKLM "Software\Pulse-Eight\USB-CEC Adapter software" + DeleteRegKey /ifempty HKLM "Software\Pulse-Eight" +SectionEnd \ No newline at end of file diff --git a/src/cec-client/CMakeLists.txt b/src/cec-client/CMakeLists.txt index 079acd59..5418ef7b 100644 --- a/src/cec-client/CMakeLists.txt +++ b/src/cec-client/CMakeLists.txt @@ -65,7 +65,9 @@ if (NOT WIN32) endif() else() add_definitions(-DTARGET_WINDOWS -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -D_WINSOCKAPI_) - if (NOT ${WIN64}) + if (${WIN64}) + string(REPLACE "/arch:SSE2" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + else() add_definitions(-D_USE_32BIT_TIME_T) endif() endif() diff --git a/src/cecc-client/CMakeLists.txt b/src/cecc-client/CMakeLists.txt index b0ee0115..80f164bc 100644 --- a/src/cecc-client/CMakeLists.txt +++ b/src/cecc-client/CMakeLists.txt @@ -35,7 +35,9 @@ if (NOT WIN32) endif() else() add_definitions(-DTARGET_WINDOWS -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -D_WINSOCKAPI_) - if (NOT ${WIN64}) + if (${WIN64}) + string(REPLACE "/arch:SSE2" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + else() add_definitions(-D_USE_32BIT_TIME_T) endif() endif() diff --git a/src/dotnet b/src/dotnet index 180cbe2b..3b371d0c 160000 --- a/src/dotnet +++ b/src/dotnet @@ -1 +1 @@ -Subproject commit 180cbe2b4e2b2d6a58c4b22d09760aba4b67a8e5 +Subproject commit 3b371d0cec8c8fe94fe6f6785ea0f2e1c59aeec1 diff --git a/src/LibCecSharp/CecSharpTypes.h b/src/dotnetlib/CecSharpTypes.h similarity index 100% rename from src/LibCecSharp/CecSharpTypes.h rename to src/dotnetlib/CecSharpTypes.h diff --git a/src/dotnetlib/LibCecSharp/.gitignore b/src/dotnetlib/LibCecSharp/.gitignore new file mode 100644 index 00000000..18f55fdd --- /dev/null +++ b/src/dotnetlib/LibCecSharp/.gitignore @@ -0,0 +1,6 @@ +AssemblyInfo.cpp +project/*.user +project/.vs +project/Release +project/Debug +project/x64 \ No newline at end of file diff --git a/src/LibCecSharp/AssemblyInfo.cpp.in b/src/dotnetlib/LibCecSharp/AssemblyInfo.cpp.in similarity index 88% rename from src/LibCecSharp/AssemblyInfo.cpp.in rename to src/dotnetlib/LibCecSharp/AssemblyInfo.cpp.in index e375cff8..ce6ce582 100644 --- a/src/LibCecSharp/AssemblyInfo.cpp.in +++ b/src/dotnetlib/LibCecSharp/AssemblyInfo.cpp.in @@ -9,7 +9,7 @@ using namespace System::Security::Permissions; [assembly:AssemblyConfigurationAttribute("")]; [assembly:AssemblyCompanyAttribute("Pulse-Eight Limited")]; [assembly:AssemblyProductAttribute("LibCecSharp")]; -[assembly:AssemblyCopyrightAttribute("Copyright (c) Pulse-Eight Limited 2011-2015")]; +[assembly:AssemblyCopyrightAttribute("Copyright (c) Pulse-Eight Limited 2011-2020")]; [assembly:AssemblyTrademarkAttribute("")]; [assembly:AssemblyCultureAttribute("")]; @@ -17,4 +17,3 @@ using namespace System::Security::Permissions; [assembly:ComVisible(false)]; [assembly:CLSCompliantAttribute(true)]; -[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)]; diff --git a/src/LibCecSharp/LibCecSharp.cpp b/src/dotnetlib/LibCecSharp/LibCecSharp.cpp similarity index 100% rename from src/LibCecSharp/LibCecSharp.cpp rename to src/dotnetlib/LibCecSharp/LibCecSharp.cpp diff --git a/project/LibCecSharp/LibCecSharp.rc.in b/src/dotnetlib/LibCecSharp/project/LibCecSharp.rc.in similarity index 98% rename from project/LibCecSharp/LibCecSharp.rc.in rename to src/dotnetlib/LibCecSharp/project/LibCecSharp.rc.in index 65bf348f..856dfe63 100644 --- a/project/LibCecSharp/LibCecSharp.rc.in +++ b/src/dotnetlib/LibCecSharp/project/LibCecSharp.rc.in @@ -21,7 +21,7 @@ BEGIN VALUE "FileDescription", "LibCecSharp" VALUE "FileVersion", "${LIBCEC_VERSION_MAJOR}.${LIBCEC_VERSION_MINOR}.${LIBCEC_VERSION_PATCH}.0" VALUE "InternalName", "LibCecSharp.dll" - VALUE "LegalCopyright", "Copyright (c) Pulse-Eight Limited 2011-2015" + VALUE "LegalCopyright", "Copyright (c) Pulse-Eight Limited 2011-2020" VALUE "OriginalFilename", "LibCecSharp.dll" VALUE "ProductName", "LibCecSharp" VALUE "ProductVersion", "${LIBCEC_VERSION_MAJOR}.${LIBCEC_VERSION_MINOR}.${LIBCEC_VERSION_PATCH}.0" diff --git a/project/LibCecSharp/LibCecSharp.vcxproj b/src/dotnetlib/LibCecSharp/project/LibCecSharp.vcxproj similarity index 80% rename from project/LibCecSharp/LibCecSharp.vcxproj rename to src/dotnetlib/LibCecSharp/project/LibCecSharp.vcxproj index 237382ab..394d24fe 100644 --- a/project/LibCecSharp/LibCecSharp.vcxproj +++ b/src/dotnetlib/LibCecSharp/project/LibCecSharp.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -22,32 +22,33 @@ {E54D4581-CD59-4687-BB10-694B8192EABA} LibCecSharp ManagedCProj - v2.0 + v4.0 + 8.1 DynamicLibrary - v90 + v142 Unicode true true DynamicLibrary - v90 + v142 Unicode true DynamicLibrary - v90 + v142 Unicode true true DynamicLibrary - v90 + v142 Unicode true @@ -74,27 +75,27 @@ $(SolutionDir)..;$(ReferencePath) $(SolutionDir)..\build\x86\ $(Configuration)\ - $(SolutionDir)..\src\libcec\platform\windows;$(OutDir)\include;$(IncludePath) + $(IncludePath);$(SolutionDir)..\src\libcec\platform\windows;$(OutDir)include $(SolutionDir)..;$(ReferencePath) $(SolutionDir)..\build\amd64\ $(Platform)\$(Configuration)\ true - $(SolutionDir)..\src\libcec\platform\windows;$(OutDir)\include;$(IncludePath) + $(IncludePath);$(SolutionDir)..\src\libcec\platform\windows;$(OutDir)include $(SolutionDir)..;$(ReferencePath) $(SolutionDir)..\build\x86\ $(Configuration)\ false - $(SolutionDir)..\src\libcec\platform\windows;$(OutDir)\include;$(IncludePath) + $(IncludePath);$(SolutionDir)..\src\libcec\platform\windows;$(OutDir)include $(SolutionDir)..;$(ReferencePath) $(SolutionDir)..\build\amd64\ $(Platform)\$(Configuration)\ - $(SolutionDir)..\src\libcec\platform\windows;$(OutDir)\include;$(IncludePath) + $(IncludePath);$(SolutionDir)..\src\libcec\platform\windows;$(OutDir)include @@ -102,7 +103,7 @@ Disabled - $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\lib\platform\windows;%(AdditionalIncludeDirectories) + $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\dotnetlib;%(AdditionalIncludeDirectories) _DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreadedDebugDLL @@ -112,7 +113,7 @@ ProgramDatabase - $(OutDir)cec.lib;%(AdditionalDependencies) + $(OutDir)cec-static.lib;$(OutDir)lib\p8-platform.lib;%(AdditionalDependencies) true true MachineX86 @@ -124,7 +125,7 @@ Disabled - $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\lib\platform\windows;%(AdditionalIncludeDirectories) + $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\dotnetlib;%(AdditionalIncludeDirectories) _DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreadedDebugDLL @@ -134,7 +135,7 @@ ProgramDatabase - $(OutDir)cec.lib;%(AdditionalDependencies) + $(OutDir)cec-static.lib;$(OutDir)lib\p8-platform.lib;%(AdditionalDependencies) true true MachineX64 @@ -145,7 +146,7 @@ X64 - $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\lib\platform\windows;%(AdditionalIncludeDirectories) + $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\dotnetlib;%(AdditionalIncludeDirectories) NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreadedDLL @@ -155,8 +156,7 @@ ProgramDatabase - $(OutDir)cec.lib;%(AdditionalDependencies) - true + $(OutDir)cec-static.lib;$(OutDir)lib\p8-platform.lib;%(AdditionalDependencies) MachineX86 @@ -165,7 +165,7 @@ X64 - $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\lib\platform\windows;%(AdditionalIncludeDirectories) + $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\dotnetlib;%(AdditionalIncludeDirectories) NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreadedDLL @@ -175,7 +175,7 @@ ProgramDatabase - $(OutDir)cec.lib;%(AdditionalDependencies) + $(OutDir)cec-static.lib;$(OutDir)lib\p8-platform.lib;%(AdditionalDependencies) @@ -193,14 +193,14 @@ - - + + - - - - + + + + diff --git a/project/LibCecSharp/LibCecSharp.vcxproj.filters b/src/dotnetlib/LibCecSharp/project/LibCecSharp.vcxproj.filters similarity index 75% rename from project/LibCecSharp/LibCecSharp.vcxproj.filters rename to src/dotnetlib/LibCecSharp/project/LibCecSharp.vcxproj.filters index bd311e7b..903f6f4e 100644 --- a/project/LibCecSharp/LibCecSharp.vcxproj.filters +++ b/src/dotnetlib/LibCecSharp/project/LibCecSharp.vcxproj.filters @@ -11,27 +11,27 @@ - + Source Files - + Source Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files diff --git a/project/LibCecSharp/resource.h b/src/dotnetlib/LibCecSharp/project/resource.h similarity index 100% rename from project/LibCecSharp/resource.h rename to src/dotnetlib/LibCecSharp/project/resource.h diff --git a/src/dotnetlib/LibCecSharpCore/.gitignore b/src/dotnetlib/LibCecSharpCore/.gitignore new file mode 100644 index 00000000..18f55fdd --- /dev/null +++ b/src/dotnetlib/LibCecSharpCore/.gitignore @@ -0,0 +1,6 @@ +AssemblyInfo.cpp +project/*.user +project/.vs +project/Release +project/Debug +project/x64 \ No newline at end of file diff --git a/src/dotnetlib/LibCecSharpCore/AssemblyInfo.cpp.in b/src/dotnetlib/LibCecSharpCore/AssemblyInfo.cpp.in new file mode 100644 index 00000000..0cc3bc39 --- /dev/null +++ b/src/dotnetlib/LibCecSharpCore/AssemblyInfo.cpp.in @@ -0,0 +1,22 @@ +#include "pch.h" + +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +[assembly:AssemblyTitleAttribute(L"LibCecSharpCore")]; +[assembly:AssemblyDescriptionAttribute(L"")]; +[assembly:AssemblyConfigurationAttribute(L"")]; +[assembly:AssemblyCompanyAttribute(L"Pulse-Eight Limited")]; +[assembly:AssemblyProductAttribute(L"LibCecSharpUWP")]; +[assembly:AssemblyCopyrightAttribute(L"Copyright (c) Pulse-Eight Limited 2011-2020")]; +[assembly:AssemblyTrademarkAttribute(L"")]; +[assembly:AssemblyCultureAttribute(L"")]; + +[assembly:AssemblyVersionAttribute("@LIBCEC_VERSION_MAJOR@.@LIBCEC_VERSION_MINOR@.@LIBCEC_VERSION_PATCH@.*")]; + +[assembly:ComVisible(false)]; + +[assembly:CLSCompliantAttribute(true)]; diff --git a/src/dotnetlib/LibCecSharpCore/LibCecSharpCore.cpp b/src/dotnetlib/LibCecSharpCore/LibCecSharpCore.cpp new file mode 100644 index 00000000..911b30ca --- /dev/null +++ b/src/dotnetlib/LibCecSharpCore/LibCecSharpCore.cpp @@ -0,0 +1,783 @@ +#include "pch.h" + +#include "LibCecSharpCore.h" +#include "CecSharpTypes.h" +#using + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace CEC; +using namespace msclr::interop; + +namespace CecSharp +{ + /// + /// 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. + /// + public ref class LibCecSharp : public CecCallbackMethods + { + public: + /// + /// Create a new LibCecSharp instance. + /// + /// The configuration to pass to libCEC. + LibCecSharp(LibCECConfiguration^ config) + { + m_callbacks = config->Callbacks; + CecCallbackMethods::EnableCallbacks(m_callbacks); + if (!InitialiseLibCec(config)) + throw gcnew Exception("Could not initialise LibCecSharp"); + } + + ~LibCecSharp(void) + { + Close(); + m_libCec = NULL; + } + + /// + /// Try to find all connected CEC adapters. + /// + /// The path filter for adapters. Leave empty to return all adapters. + /// The adapters that were found. + array^ FindAdapters(String^ path) + { + cec_adapter_descriptor* devices = new cec_adapter_descriptor[10]; + + marshal_context^ context = gcnew marshal_context(); + const char* strPathC = path->Length > 0 ? context->marshal_as(path) : NULL; + + uint8_t iDevicesFound = m_libCec->DetectAdapters(devices, 10, NULL, false); + + array^ adapters = gcnew array(iDevicesFound); + for (unsigned int iPtr = 0; iPtr < iDevicesFound; iPtr++) + adapters[iPtr] = gcnew CecAdapter(gcnew String(devices[iPtr].strComPath), gcnew String(devices[iPtr].strComName), devices[iPtr].iVendorId, devices[iPtr].iProductId, devices[iPtr].iFirmwareVersion, devices[iPtr].iFirmwareBuildDate, devices[iPtr].iPhysicalAddress); + + delete devices; + delete context; + return adapters; + } + + /// + /// Open a connection to the CEC adapter. + /// + /// The COM port of the adapter + /// Connection timeout in milliseconds + /// True when a connection was opened, false otherwise. + 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(strPort); + bool bReturn = m_libCec->Open(strPortC, iTimeoutMs); + delete context; + return bReturn; + } + + /// + /// Close the connection to the CEC adapter + /// + void Close(void) + { + DisableCallbacks(); + m_libCec->Close(); + } + + /// + /// Disable all calls to callback methods. + /// + 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); + } + + /// + /// Enable or change the callback methods that libCEC uses to send changes to the client application. + /// + /// The new callback methods to use. + /// True when the callbacks were changed, false otherwise + virtual bool EnableCallbacks(CecCallbackMethods^ callbacks) override + { + if (m_libCec && CecCallbackMethods::EnableCallbacks(callbacks)) + return m_libCec->EnableCallbacks((void*)GetCallbackPtr(), &g_cecCallbacks); + + return false; + } + + /// + /// Sends a ping command to the adapter, to check if it's responding. + /// + /// True when the ping was successful, false otherwise + bool PingAdapter(void) + { + return m_libCec->PingAdapter(); + } + + /// + /// Start the bootloader of the CEC adapter. Closes the connection when successful. + /// + /// True when the command was sent successfully, false otherwise. + bool StartBootloader(void) + { + return m_libCec->StartBootloader(); + } + + /// + /// Transmit a raw CEC command over the CEC line. + /// + /// The command to transmit + /// True when the data was sent and acked, false otherwise. + 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); + } + + /// + /// 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. + /// + /// The CEC adapter's new logical address. + /// True when the logical address was set successfully, false otherwise. + bool SetLogicalAddress(CecLogicalAddress logicalAddress) + { + return m_libCec->SetLogicalAddress((cec_logical_address)logicalAddress); + } + + /// + /// 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. + /// + /// The CEC adapter's new physical address. + /// True when the physical address was set successfully, false otherwise. + bool SetPhysicalAddress(uint16_t physicalAddress) + { + return m_libCec->SetPhysicalAddress(physicalAddress); + } + + /// + /// Power on the given CEC capable devices. If CECDEVICE_BROADCAST is used, then wakeDevice in libcec_configuration will be used. + /// + /// The logical address to power on. + /// True when the command was sent successfully, false otherwise. + bool PowerOnDevices(CecLogicalAddress logicalAddress) + { + return m_libCec->PowerOnDevices((cec_logical_address)logicalAddress); + } + + /// + /// Put the given CEC capable devices in standby mode. If CECDEVICE_BROADCAST is used, then standbyDevices in libcec_configuration will be used. + /// + /// The logical address of the device to put in standby. + /// True when the command was sent successfully, false otherwise. + bool StandbyDevices(CecLogicalAddress logicalAddress) + { + return m_libCec->StandbyDevices((cec_logical_address)logicalAddress); + } + + /// + /// Sends a POLL message to a device, to check if it's present and responding. + /// + /// The device to send the message to. + /// True if the POLL was acked, false otherwise. + bool PollDevice(CecLogicalAddress logicalAddress) + { + return m_libCec->PollDevice((cec_logical_address)logicalAddress); + } + + /// + /// 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. + /// + /// The new active source. Use CEC_DEVICE_TYPE_RESERVED to use the primary type + /// True when the command was sent successfully, false otherwise. + bool SetActiveSource(CecDeviceType type) + { + return m_libCec->SetActiveSource((cec_device_type)type); + } + + /// + /// Change the deck control mode, if this adapter is registered as playback or recording device. + /// + /// The new control mode. + /// True to send the new status over the CEC line. + /// True if set, false otherwise. + bool SetDeckControlMode(CecDeckControlMode mode, bool sendUpdate) + { + return m_libCec->SetDeckControlMode((cec_deck_control_mode)mode, sendUpdate); + } + + /// + /// Change the deck info, if this adapter is a playback or recording device. + /// + /// The new deck info. + /// True to send the new status over the CEC line. + /// True if set, false otherwise. + bool SetDeckInfo(CecDeckInfo info, bool sendUpdate) + { + return m_libCec->SetDeckInfo((cec_deck_info)info, sendUpdate); + } + + /// + /// Broadcast a message that notifies connected CEC capable devices that this device is no longer the active source. + /// + /// True when the command was sent successfully, false otherwise. + bool SetInactiveView(void) + { + return m_libCec->SetInactiveView(); + } + + /// + /// Change the menu state. This value is already changed by libCEC automatically if a device is (de)activated. + /// + /// The new state. + /// True to send the new status over the CEC line. + /// True if set, false otherwise. + bool SetMenuState(CecMenuState state, bool sendUpdate) + { + return m_libCec->SetMenuState((cec_menu_state)state, sendUpdate); + } + + /// + /// Display a message on the device with the given logical address. Not supported by most TVs. + /// + /// The logical address of the device to display the message on. + /// The duration of the message + /// The message to display. + /// True when the command was sent, false otherwise. + bool SetOSDString(CecLogicalAddress logicalAddress, CecDisplayControl duration, String^ message) + { + marshal_context^ context = gcnew marshal_context(); + const char* strMessageC = context->marshal_as(message); + + bool bReturn = m_libCec->SetOSDString((cec_logical_address)logicalAddress, (cec_display_control)duration, strMessageC); + + delete context; + return bReturn; + } + + /// + /// 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. + /// + /// True to enable, false to disable. + /// True when switched successfully, false otherwise. + bool SwitchMonitoring(bool enable) + { + return m_libCec->SwitchMonitoring(enable); + } + + /// + /// Get the CEC version of the device with the given logical address + /// + /// The logical address of the device to get the CEC version for. + /// The version or CEC_VERSION_UNKNOWN when the version couldn't be fetched. + CecVersion GetDeviceCecVersion(CecLogicalAddress logicalAddress) + { + return (CecVersion)m_libCec->GetDeviceCecVersion((cec_logical_address)logicalAddress); + } + + /// + /// Get the menu language of the device with the given logical address + /// + /// The logical address of the device to get the menu language for. + /// The requested menu language. + String^ GetDeviceMenuLanguage(CecLogicalAddress logicalAddress) + { + std::string strLang = m_libCec->GetDeviceMenuLanguage((cec_logical_address)logicalAddress); + return gcnew String(strLang.c_str()); + } + + /// + /// Get the vendor ID of the device with the given logical address. + /// + /// The logical address of the device to get the vendor ID for. + /// The vendor ID or 0 if it wasn't found. + CecVendorId GetDeviceVendorId(CecLogicalAddress logicalAddress) + { + return (CecVendorId)m_libCec->GetDeviceVendorId((cec_logical_address)logicalAddress); + } + + /// + /// Get the power status of the device with the given logical address. + /// + /// The logical address of the device to get the power status for. + /// The power status or CEC_POWER_STATUS_UNKNOWN if it wasn't found. + CecPowerStatus GetDevicePowerStatus(CecLogicalAddress logicalAddress) + { + return (CecPowerStatus)m_libCec->GetDevicePowerStatus((cec_logical_address)logicalAddress); + } + + /// + /// Tell libCEC to poll for active devices on the bus. + /// + void RescanActiveDevices(void) + { + m_libCec->RescanActiveDevices(); + } + + /// + /// Get the logical addresses of the devices that are active on the bus, including those handled by libCEC. + /// + /// The logical addresses of the active devices + 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->Set((CecLogicalAddress)iPtr); + + return retVal; + } + + /// + /// Check whether a device is active on the bus. + /// + /// The address to check. + /// True when active, false otherwise. + bool IsActiveDevice(CecLogicalAddress logicalAddress) + { + return m_libCec->IsActiveDevice((cec_logical_address)logicalAddress); + } + + /// + /// Check whether a device of the given type is active on the bus. + /// + /// The type to check. + /// True when active, false otherwise. + bool IsActiveDeviceType(CecDeviceType type) + { + return m_libCec->IsActiveDeviceType((cec_device_type)type); + } + + /// + /// Changes the active HDMI port. + /// + /// The device to which this libCEC is connected. + /// The new port number. + /// True when changed, false otherwise. + bool SetHDMIPort(CecLogicalAddress address, uint8_t port) + { + return m_libCec->SetHDMIPort((cec_logical_address)address, port); + } + + /// + /// Sends a volume up keypress to an audiosystem if it's present. + /// + /// Send a key release after the keypress. + /// The new audio status. + uint8_t VolumeUp(bool sendRelease) + { + return m_libCec->VolumeUp(sendRelease); + } + + /// + /// Sends a volume down keypress to an audiosystem if it's present. + /// + /// Send a key release after the keypress. + /// The new audio status. + uint8_t VolumeDown(bool sendRelease) + { + return m_libCec->VolumeDown(sendRelease); + } + + /// + /// Sends a mute keypress to an audiosystem if it's present. + /// + /// The new audio status. + uint8_t MuteAudio() + { + return m_libCec->AudioToggleMute(); + } + + /// + /// Send a keypress to a device on the CEC bus. + /// + /// The logical address of the device to send the message to. + /// The key to send. + /// True to wait for a response, false otherwise. + /// True when the keypress was acked, false otherwise. + bool SendKeypress(CecLogicalAddress destination, CecUserControlCode key, bool wait) + { + return m_libCec->SendKeypress((cec_logical_address)destination, (cec_user_control_code)key, wait); + } + + /// + /// Send a key release to a device on the CEC bus. + /// + /// The logical address of the device to send the message to. + /// True to wait for a response, false otherwise. + /// True when the key release was acked, false otherwise. + bool SendKeyRelease(CecLogicalAddress destination, bool wait) + { + return m_libCec->SendKeyRelease((cec_logical_address)destination, wait); + } + + /// + /// Get the OSD name of a device on the CEC bus. + /// + /// The logical address of the device to get the OSD name for. + /// The OSD name. + String^ GetDeviceOSDName(CecLogicalAddress logicalAddress) + { + std::string osdName = 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]; + strncpy(strOsdName, osdName.c_str(), 15); + return gcnew String(strOsdName); + } + + /// + /// Get the logical address of the device that is currently the active source on the CEC bus. + /// + /// The active source or CECDEVICE_UNKNOWN when unknown. + CecLogicalAddress GetActiveSource() + { + return (CecLogicalAddress)m_libCec->GetActiveSource(); + } + + /// + /// Check whether a device is currently the active source on the CEC bus. + /// + /// The logical address of the device to check. + /// True when it is the active source, false otherwise. + bool IsActiveSource(CecLogicalAddress logicalAddress) + { + return m_libCec->IsActiveSource((cec_logical_address)logicalAddress); + } + + /// + /// Get the physical address of the device with the given logical address. + /// + /// The logical address of the device to get the physical address for. + /// The physical address or 0 if it wasn't found. + uint16_t GetDevicePhysicalAddress(CecLogicalAddress address) + { + return m_libCec->GetDevicePhysicalAddress((cec_logical_address)address); + } + + /// + /// Sets the stream path to the device on the given logical address. + /// + /// The address to activate. + /// True when the command was sent, false otherwise. + bool SetStreamPath(CecLogicalAddress address) + { + return m_libCec->SetStreamPath((cec_logical_address)address); + } + + /// + /// Sets the stream path to the device on the given physical address. + /// + /// The address to activate. + /// True when the command was sent, false otherwise. + bool SetStreamPath(uint16_t physicalAddress) + { + return m_libCec->SetStreamPath(physicalAddress); + } + + /// + /// Get the list of logical addresses that libCEC is controlling + /// + /// The list of logical addresses that libCEC is controlling + 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; + } + + /// + /// Get libCEC's current configuration. + /// + /// The configuration. + /// True when the configuration was updated, false otherwise. + bool GetCurrentConfiguration(LibCECConfiguration^ configuration) + { + libcec_configuration config; + config.Clear(); + + if (m_libCec->GetCurrentConfiguration(&config)) + { + configuration->Update(config); + return true; + } + return false; + } + + /// + /// Check whether the CEC adapter can persist a configuration. + /// + /// True when this CEC adapter can persist the user configuration, false otherwise. + bool CanPersistConfiguration(void) + { + return m_libCec->CanPersistConfiguration(); + } + + /// + /// Persist the given configuration in adapter (if supported) + /// + /// The configuration to store. + /// True when the configuration was persisted, false otherwise. + bool PersistConfiguration(LibCECConfiguration^ configuration) + { + marshal_context^ context = gcnew marshal_context(); + libcec_configuration config; + ConvertConfiguration(context, configuration, config); + + bool bReturn = m_libCec->PersistConfiguration(&config); + + delete context; + return bReturn; + } + + /// + /// Change libCEC's configuration. + /// + /// The new configuration. + /// True when the configuration was changed successfully, false otherwise. + bool SetConfiguration(LibCECConfiguration^ configuration) + { + marshal_context^ context = gcnew marshal_context(); + libcec_configuration config; + ConvertConfiguration(context, configuration, config); + + bool bReturn = m_libCec->SetConfiguration(&config); + + delete context; + return bReturn; + } + + /// + /// Check whether libCEC is the active source on the bus. + /// + /// True when libCEC is the active source on the bus, false otherwise. + bool IsLibCECActiveSource() + { + return m_libCec->IsLibCECActiveSource(); + } + + /// + /// Get information about the given CEC adapter. + /// + /// The COM port to which the device is connected + /// The device configuration + /// The timeout in milliseconds + /// True when the device was found, false otherwise + bool GetDeviceInformation(String^ port, LibCECConfiguration^ configuration, uint32_t timeoutMs) + { + bool bReturn(false); + marshal_context^ context = gcnew marshal_context(); + + libcec_configuration config; + config.Clear(); + + const char* strPortC = port->Length > 0 ? context->marshal_as(port) : NULL; + + if (m_libCec->GetDeviceInformation(strPortC, &config, timeoutMs)) + { + configuration->Update(config); + bReturn = true; + } + + delete context; + return bReturn; + } + + String^ ToString(CecLogicalAddress iAddress) + { + const char* retVal = m_libCec->ToString((cec_logical_address)iAddress); + 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(CecMenuState iState) + { + const char* retVal = m_libCec->ToString((cec_menu_state)iState); + return gcnew String(retVal); + } + + String^ ToString(CecDeckControlMode iMode) + { + const char* retVal = m_libCec->ToString((cec_deck_control_mode)iMode); + return gcnew String(retVal); + } + + String^ ToString(CecDeckInfo status) + { + const char* retVal = m_libCec->ToString((cec_deck_info)status); + return gcnew String(retVal); + } + + String^ ToString(CecOpcode opcode) + { + const char* retVal = m_libCec->ToString((cec_opcode)opcode); + return gcnew String(retVal); + } + + String^ ToString(CecSystemAudioStatus mode) + { + const char* retVal = m_libCec->ToString((cec_system_audio_status)mode); + return gcnew String(retVal); + } + + String^ ToString(CecAudioStatus status) + { + const char* retVal = m_libCec->ToString((cec_audio_status)status); + return gcnew String(retVal); + } + + String^ VersionToString(uint32_t version) + { + char buf[20]; + m_libCec->PrintVersion(version, buf, 20); + return gcnew String(buf); + } + + String^ PhysicalAddressToString(uint16_t physicalAddress) + { + char buf[8]; + snprintf(buf, 8, "%X.%X.%X.%X", (physicalAddress >> 12) & 0xF, (physicalAddress >> 8) & 0xF, (physicalAddress >> 4) & 0xF, physicalAddress & 0xF); + return gcnew String(buf); + } + + /// + /// Get a string with information about how libCEC was compiled. + /// + /// A string with information about how libCEC was compiled. + String^ GetLibInfo() + { + const char* retVal = m_libCec->GetLibInfo(); + return gcnew String(retVal); + } + + /// + /// 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. + /// + /// Should be called as first call to libCEC, directly after CECInitialise() and before using Open() + void InitVideoStandalone() + { + m_libCec->InitVideoStandalone(); + } + + /// + /// Get the (virtual) USB vendor id + /// + /// The (virtual) USB vendor id + uint16_t GetAdapterVendorId() + { + return m_libCec->GetAdapterVendorId(); + } + + /// + /// Get the (virtual) USB product id + /// + /// The (virtual) USB product id + uint16_t GetAdapterProductId() + { + return m_libCec->GetAdapterProductId(); + } + + private: + !LibCecSharp(void) + { + Close(); + m_libCec = NULL; + } + + bool InitialiseLibCec(LibCECConfiguration^ config) + { + marshal_context^ context = gcnew marshal_context(); + libcec_configuration libCecConfig; + ConvertConfiguration(context, config, libCecConfig); + + m_libCec = (ICECAdapter*)CECInitialise(&libCecConfig); + config->Update(libCecConfig); + + delete context; + return m_libCec != NULL; + } + + void ConvertConfiguration(marshal_context^ context, LibCECConfiguration^ netConfig, CEC::libcec_configuration& config) + { + config.Clear(); + + const char* strDeviceName = context->marshal_as(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 = 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.bPowerOffOnStandby = netConfig->PowerOffOnStandby ? 1 : 0; + const char* strDeviceLanguage = context->marshal_as(netConfig->DeviceLanguage); + memcpy_s(config.strDeviceLanguage, 3, strDeviceLanguage, 3); + config.bMonitorOnly = netConfig->MonitorOnlyClient ? 1 : 0; + config.cecVersion = (cec_version)netConfig->CECVersion; + config.callbacks = &g_cecCallbacks; + } + + + ICECAdapter* m_libCec; + CecCallbackMethods^ m_callbacks; + }; +} diff --git a/src/dotnetlib/LibCecSharpCore/LibCecSharpCore.h b/src/dotnetlib/LibCecSharpCore/LibCecSharpCore.h new file mode 100644 index 00000000..f446616c --- /dev/null +++ b/src/dotnetlib/LibCecSharpCore/LibCecSharpCore.h @@ -0,0 +1,10 @@ +#pragma once + +//using namespace System; +// +//namespace LibCecSharpUWP { +// public ref class Class1 +// { +// // TODO: Add your methods for this class here. +// }; +//} diff --git a/src/dotnetlib/LibCecSharpCore/project/LibCecSharpCore.vcxproj b/src/dotnetlib/LibCecSharpCore/project/LibCecSharpCore.vcxproj new file mode 100644 index 00000000..16c5bfc1 --- /dev/null +++ b/src/dotnetlib/LibCecSharpCore/project/LibCecSharpCore.vcxproj @@ -0,0 +1,169 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {E8C30CBD-64D1-44F8-9172-82B728986DCC} + NetCoreCProj + LibCecSharpCore + 8.1 + netcoreapp3.1 + + + + DynamicLibrary + true + v142 + NetCore + Unicode + + + DynamicLibrary + false + v142 + NetCore + Unicode + + + DynamicLibrary + true + v142 + NetCore + Unicode + + + DynamicLibrary + false + v142 + NetCore + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)..\build\x86\netcore\ + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\src\libcec\platform\windows;$(OutDir)..\include + + + $(SolutionDir)..\build\x86\netcore\ + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\src\libcec\platform\windows;$(OutDir)..\include + + + $(SolutionDir)..\build\amd64\netcore\ + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\src\libcec\platform\windows;$(OutDir)..\include + + + $(SolutionDir)..\build\amd64\netcore\ + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\src\libcec\platform\windows;$(OutDir)..\include + + + + Use + pch.h + Level3 + WIN32;NDEBUG;%(PreprocessorDefinitions) + $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\dotnetlib;%(AdditionalIncludeDirectories) + true + + + $(OutDir)..\cec-static.lib;$(OutDir)..\lib\p8-platform.lib;%(AdditionalDependencies) + + + + + Use + pch.h + Level3 + WIN32;_HAS_ITERATOR_DEBUGGING=0;_DEBUG;%(PreprocessorDefinitions) + $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\dotnetlib;%(AdditionalIncludeDirectories) + + + $(OutDir)..\cec-static.lib;$(OutDir)..\lib\p8-platform.lib;%(AdditionalDependencies) + + + + + Use + pch.h + Level3 + _DEBUG;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions) + $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\dotnetlib;%(AdditionalIncludeDirectories) + + + $(OutDir)..\cec-static.lib;$(OutDir)..\lib\p8-platform.lib;%(AdditionalDependencies) + + + + + Use + pch.h + Level3 + NDEBUG;%(PreprocessorDefinitions) + $(SolutionDir)..\include;$(SolutionDir)..\src;$(SolutionDir)..\src\dotnetlib;%(AdditionalIncludeDirectories) + true + + + $(OutDir)..\cec-static.lib;$(OutDir)..\lib\p8-platform.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dotnetlib/LibCecSharpCore/project/LibCecSharpCore.vcxproj.filters b/src/dotnetlib/LibCecSharpCore/project/LibCecSharpCore.vcxproj.filters new file mode 100644 index 00000000..97f4f781 --- /dev/null +++ b/src/dotnetlib/LibCecSharpCore/project/LibCecSharpCore.vcxproj.filters @@ -0,0 +1,58 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/dotnetlib/LibCecSharpCore/project/Resource.h b/src/dotnetlib/LibCecSharpCore/project/Resource.h new file mode 100644 index 00000000..d5ac7c42 --- /dev/null +++ b/src/dotnetlib/LibCecSharpCore/project/Resource.h @@ -0,0 +1,3 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by app.rc diff --git a/src/dotnetlib/LibCecSharpCore/project/app.ico b/src/dotnetlib/LibCecSharpCore/project/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..dc1baae96a830e8f8a23b432e73dc6201073d1d2 GIT binary patch literal 370070 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 RS->n{7BCB#1^T!J{vW0JHckKl literal 0 HcmV?d00001 diff --git a/src/dotnetlib/LibCecSharpCore/project/pch.cpp b/src/dotnetlib/LibCecSharpCore/project/pch.cpp new file mode 100644 index 00000000..64b7eef6 --- /dev/null +++ b/src/dotnetlib/LibCecSharpCore/project/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/dotnetlib/LibCecSharpCore/project/pch.h b/src/dotnetlib/LibCecSharpCore/project/pch.h new file mode 100644 index 00000000..9d715b06 --- /dev/null +++ b/src/dotnetlib/LibCecSharpCore/project/pch.h @@ -0,0 +1,12 @@ +// pch.h: This is a precompiled header file. +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef PCH_H +#define PCH_H + +// add headers that you want to pre-compile here + +#endif //PCH_H diff --git a/src/libcec/CMakeLists.txt b/src/libcec/CMakeLists.txt index e3718b40..19a070b7 100644 --- a/src/libcec/CMakeLists.txt +++ b/src/libcec/CMakeLists.txt @@ -159,6 +159,12 @@ if (WIN32) # write libcec.rc configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libcec.rc.in ${CMAKE_CURRENT_SOURCE_DIR}/libcec.rc) add_definitions(-DDLL_EXPORT) + add_definitions(-DTARGET_WINDOWS -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -D_WINSOCKAPI_) + if (${WIN64}) + string(REPLACE "/arch:SSE2" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + else() + add_definitions(-D_USE_32BIT_TIME_T) + endif() else() # write pkgconfig include(../../cmake/PkgConfigHandler.cmake) @@ -181,14 +187,51 @@ install(FILES ${PROJECT_SOURCE_DIR}/../../include/cec.h ${PROJECT_SOURCE_DIR}/../../include/version.h DESTINATION include/libcec) -# libCEC target +# external dependencies +include(cmake/LinkPlatformSupport.cmake) + +# libCEC shared target add_library(cec SHARED ${CEC_SOURCES}) install(TARGETS cec DESTINATION ${LIB_DESTINATION}) set_target_properties(cec PROPERTIES VERSION ${LIBCEC_VERSION_MAJOR}.${LIBCEC_VERSION_MINOR}.${LIBCEC_VERSION_PATCH} SOVERSION ${LIBCEC_VERSION_MAJOR}) -target_link_libraries(cec ${p8-platform_LIBRARIES}) -target_link_libraries(cec ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(cec ${cec_depends}) + +if (MSVC) + # generate pdb in release mode too + set_target_properties(cec + PROPERTIES + COMPILE_PDB_NAME_DEBUG cec${CMAKE_DEBUG_POSTFIX} + COMPILE_PDB_NAME_RELEASE cec + COMPILE_PDB_NAME_MINSIZEREL cec + COMPILE_PDB_NAME_RELWITHDEBINFO cec) +endif(MSVC) + +if(WIN32) + # libCEC static target used by .net wrappers + add_library(cec-static STATIC ${CEC_SOURCES}) + install(TARGETS cec-static + DESTINATION ${LIB_DESTINATION}) + set_target_properties(cec-static PROPERTIES VERSION ${LIBCEC_VERSION_MAJOR}.${LIBCEC_VERSION_MINOR}.${LIBCEC_VERSION_PATCH} + SOVERSION ${LIBCEC_VERSION_MAJOR}) + target_link_libraries(cec-static ${cec_depends}) + + if (MSVC) + # generate pdb in release mode too + set_target_properties(cec-static + PROPERTIES + COMPILE_PDB_NAME_DEBUG cec-static${CMAKE_DEBUG_POSTFIX} + COMPILE_PDB_NAME_RELEASE cec-static + COMPILE_PDB_NAME_MINSIZEREL cec-static + COMPILE_PDB_NAME_RELWITHDEBINFO cec-static) + + # install generated pdb + install(FILES $/cec.pdb + DESTINATION "${CMAKE_INSTALL_LIBDIR}") + install(FILES $/cec-static.pdb + DESTINATION "${CMAKE_INSTALL_LIBDIR}") + endif(MSVC) +endif(WIN32) -include(cmake/LinkPlatformSupport.cmake) include(cmake/DisplayPlatformSupport.cmake) diff --git a/src/libcec/cmake/LinkPlatformSupport.cmake b/src/libcec/cmake/LinkPlatformSupport.cmake index fc273532..e582e8f2 100644 --- a/src/libcec/cmake/LinkPlatformSupport.cmake +++ b/src/libcec/cmake/LinkPlatformSupport.cmake @@ -1,40 +1,45 @@ # - Link platform support dependencies found by CheckPlatformSupport.cmake +list(APPEND cec_depends ${p8-platform_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT}) + # lockdev if (HAVE_LOCKDEV) - target_link_libraries(cec lockdev) + list(APPEND cec_depends lockdev) endif() # udev if (HAVE_LIBUDEV) - target_link_libraries(cec udev) + list(APPEND cec_depends udev) endif() # xrandr if (HAVE_RANDR) - target_link_libraries(cec Xrandr) - target_link_libraries(cec X11) + list(APPEND cec_depends Xrandr + X11) endif() # rt if (HAVE_RT) - target_link_libraries(cec rt) + list(APPEND cec_depends rt) endif() # dl if (HAVE_DLOPEN) - target_link_libraries(cec dl) + list(APPEND cec_depends dl) endif() # raspberry pi if (HAVE_RPI_API) - target_link_libraries(cec ${RPI_VCOS} ${RPI_VCHIQ_ARM} ${RPI_BCM_HOST}) + list(APPEND cec_depends ${RPI_VCOS} + ${RPI_VCHIQ_ARM} + ${RPI_BCM_HOST}) endif() # Apple if (APPLE) - target_link_libraries(cec "-framework CoreFoundation") - target_link_libraries(cec "-framework IOKit") - target_link_libraries(cec "-framework CoreVideo") + list(APPEND cec_depends "-framework CoreFoundation" + "-framework IOKit" + "-framework CoreVideo") endif() diff --git a/src/platform b/src/platform index 6535e48d..61976431 160000 --- a/src/platform +++ b/src/platform @@ -1 +1 @@ -Subproject commit 6535e48d68d69264c32d46ab9386ac18f77da5f7 +Subproject commit 6197643150d1cebc4d686300193402bc979618ab diff --git a/support b/support index 63ec40a9..d0a6b566 160000 --- a/support +++ b/support @@ -1 +1 @@ -Subproject commit 63ec40a9e2dbe26d49dba0a73071a410dcc6b73b +Subproject commit d0a6b566f9723fbab971cbcc17696f6da06bf879 diff --git a/windows/build-all.cmd b/windows/build-all.cmd new file mode 100644 index 00000000..d39e3dca --- /dev/null +++ b/windows/build-all.cmd @@ -0,0 +1,128 @@ +@ECHO OFF + +rem Build a libCEC, LibCecSharp, LibCecSharpCore and client applications +rem Usage: build-all.cmd [architecture] [build type] [visual studio version] + +SETLOCAL + +SET MYDIR=%~dp0 + +rem optional parameter: architecture (amd64) +SET RUNTIMEARCH=amd64 +IF "%PROCESSOR_ARCHITECTURE%"=="x86" IF "%PROCESSOR_ARCHITEW6432%"=="" ( + SET RUNTIMEARCH=x86 +) +IF "%1" == "" ( + SET BUILDARCH=%RUNTIMEARCH% +) ELSE ( + SET BUILDARCH=%1 +) +IF "%BUILDARCH%" == "amd64" ( + SET BUILDARCHPROJECT=x64 +) ELSE ( + SET BUILDARCHPROJECT=%BUILDARCH% +) + +rem optional parameter: build type (Release) +IF "%2" == "" ( + SET BUILDTYPE=Release +) ELSE ( + SET BUILDTYPE=%2 +) + +rem optional parameter: visual studio version (2019) +IF "%3" == "" ( + SET VSVERSION=2019 +) ELSE ( + SET VSVERSION=%3 +) + +SET BUILDPATH=%MYDIR%..\build +SET EXITCODE=1 + +rem Create build dir +IF NOT EXIST "%MYDIR%..\build" ( + MKDIR "%MYDIR%..\build" >nul +) + +rem Compile libCEC +ECHO. * compiling libCEC libraries for %BUILDARCH% +CD "%MYDIR%..\project" +CALL "%MYDIR%build-lib.cmd" %BUILDARCH% %BUILDTYPE% %VSVERSION% "%BUILDPATH%" nmake +IF %errorlevel% neq 0 ( + ECHO. *** failed to build libCEC for %BUILDARCH% *** + PAUSE + EXIT /b 1 +) + +rem Set up the toolchain +CALL "%MYDIR%..\support\windows\config\toolchain.cmd" >nul +IF "%TOOLCHAIN_NAME%" == "" ( + ECHO.*** Visual Studio toolchain could not be configured for %BUILDARCH% *** + ECHO. + ECHO.See docs\README.windows.md + EXIT /b 2 +) + +rem Compile LibCecSharp and LibCecSharpCore +ECHO. * cleaning LibCecSharp and LibCecSharpCore for %BUILDARCH% +"%DevEnvDir%devenv.com" libcec.sln /Clean "%BUILDTYPE%|%BUILDARCHPROJECT%" +ECHO. * compiling LibCecSharp and LibCecSharpCore for %BUILDARCH% +"%DevEnvDir%devenv.com" libcec.sln /Build "%BUILDTYPE%|%BUILDARCHPROJECT%" + +rem Create dir for referenced libs and check compilation results +RMDIR /s /q "%MYDIR%..\build\ref" >nul 2>&1 +MKDIR "%MYDIR%..\build\ref" >nul +MKDIR "%MYDIR%..\build\ref\netcore" >nul + +rem Check and copy LibCecSharp +IF EXIST "%MYDIR%..\build\%BUILDARCH%\LibCecSharp.dll" ( + COPY "%MYDIR%..\build\%BUILDARCH%\LibCecSharp.*" "%MYDIR%..\build\ref" >nul +) ELSE ( + ECHO. *** failed to build LibCecSharp for %BUILDARCH% *** + PAUSE + EXIT /b 1 +) + +rem Check and copy LibCecSharpCore +IF EXIST "%MYDIR%..\build\%BUILDARCH%\netcore\LibCecSharpCore.dll" ( + COPY "%MYDIR%..\build\%BUILDARCH%\netcore\LibCecSharpCore.*" "%MYDIR%..\build\ref\netcore\." >nul +) ELSE ( + ECHO. *** failed to build LibCecSharpCore for %BUILDARCH% *** + PAUSE + EXIT /b 1 +) + +rem Compile cec-tray and CecSharpTester apps +ECHO. * compiling .Net applications for %BUILDARCH% +CD "%MYDIR%..\src\dotnet\project" +"%DevEnvDir%devenv.com" cec-dotnet.sln /Build "%BUILDTYPE%|%BUILDARCHPROJECT%" + +rem Check and copy CecSharpTester +IF EXIST ..\build\%BUILDARCHPROJECT%\CecSharpTester.exe ( + COPY ..\build\%BUILDARCHPROJECT%\CecSharpTester.exe "%MYDIR%..\build\%BUILDARCH%\CecSharpTester.exe" >nul +) ELSE ( + ECHO. *** failed to build CecSharpTester for %BUILDARCH% *** + PAUSE + EXIT /b 1 +) + +rem Check and copy cec-tray +IF EXIST ..\build\%BUILDARCHPROJECT%\cec-tray.exe ( + COPY ..\build\%BUILDARCHPROJECT%\cec-tray.exe "%MYDIR%..\build\%BUILDARCH%\cec-tray.exe" >nul +) ELSE ( + ECHO. *** failed to build cec-tray for %BUILDARCH% *** + PAUSE + EXIT /b 1 +) + +rem Check and copy CecSharpCoreTester +IF EXIST ..\build\%BUILDARCHPROJECT%\netcoreapp3.1\CecSharpCoreTester.exe ( + COPY ..\build\%BUILDARCHPROJECT%\netcoreapp3.1\CecSharpCoreTester.* "%MYDIR%..\build\%BUILDARCH%\netcore\." >nul +) ELSE ( + ECHO. *** failed to build CecSharpCoreTester for %BUILDARCH% *** + PAUSE + EXIT /b 1 +) + +RMDIR /s /q "%BUILDPATH%\cmake" >nul 2>&1 diff --git a/windows/build-lib.cmd b/windows/build-lib.cmd index 026f6264..39122e16 100644 --- a/windows/build-lib.cmd +++ b/windows/build-lib.cmd @@ -1,6 +1,7 @@ @ECHO OFF -rem Build libCEC for Windows +rem Build libCEC cmake projects for Windows +rem Usage: build-all.cmd [architecture] [type] [vs version] [install path] [project type] SETLOCAL @@ -12,32 +13,54 @@ SET INSTALLPATH=%~4 SET GENTYPE=%5 IF [%5] == [] GOTO missingparams +SET INSTALLPATH=%INSTALLPATH:"=% SET BUILDTARGET=%INSTALLPATH%\cmake\%BUILDARCH% SET TARGET=%INSTALLPATH%\%BUILDARCH% +rem Check support submodule +IF NOT EXIST "%MYDIR%..\support\windows\cmake\build.cmd" ( + ECHO.*** support git submodule has not been checked out *** + ECHO. + ECHO.See docs\README.windows.md + EXIT /b 2 +) + +rem Check platform submodule IF NOT EXIST "%MYDIR%..\src\platform\windows\build.cmd" ( - ECHO "platform git submodule was not checked out" - GOTO exit + ECHO.*** platform git submodule has not been checked out *** + ECHO. + ECHO.See docs\README.windows.md + EXIT /b 2 ) -ECHO Build platform library for %BUILDARCH% +rem Compile platform library +ECHO. * compiling platform library for %BUILDARCH% CALL "%MYDIR%..\src\platform\windows\build-lib.cmd" %BUILDARCH% %BUILDTYPE% %VSVERSION% "%INSTALLPATH%" -del /s /f /q "%BUILDTARGET%" +RMDIR /s /q "%BUILDTARGET%" >nul 2>&1 -ECHO Build libCEC for %BUILDARCH% +rem Compile libCEC +ECHO. * compiling libCEC for %BUILDARCH% CALL "%MYDIR%..\support\windows\cmake\generate.cmd" %BUILDARCH% %GENTYPE% "%MYDIR%.." "%BUILDTARGET%" "%TARGET%" %BUILDTYPE% %VSVERSION% +IF %errorlevel% neq 0 EXIT /b %errorlevel% + IF "%GENTYPE%" == "nmake" ( CALL "%MYDIR%..\support\windows\cmake\build.cmd" %BUILDARCH% "%BUILDTARGET%" %VSVERSION% IF NOT EXIST "%TARGET%\cec.dll" ( - echo "Failed to build %TARGET%\cec.dll" - exit /b 1 + ECHO. *** failed to build %TARGET%\cec.dll for %BUILDARCH% *** + EXIT /b 1 ) + ECHO. * libCEC for %BUILDARCH% built successfully ) -exit /b 0 +EXIT /b 0 :missingparams -ECHO "build-lib.cmd requires 4 parameters" - -:exit -exit 1 \ No newline at end of file +ECHO.%~dp0 requires 5 parameters +ECHO. %~dp0 [architecture] [type] [vs version] [install path] [project type] +ECHO. +ECHO. architecture: amd64 x86 +ECHO. build type: Release Debug +ECHO. vs version: Visual Studio version (2019) +ECHO. install path: installation path without quotes +ECHO. project type: nmake vs +EXIT /b 99 diff --git a/windows/build.cmd b/windows/build.cmd deleted file mode 100644 index d20cb676..00000000 --- a/windows/build.cmd +++ /dev/null @@ -1,39 +0,0 @@ -@ECHO OFF - -rem Build libCEC for Windows - -SETLOCAL - -SET MYDIR=%~dp0 -SET BUILDTYPE=Release -SET VSVERSION=14 -SET INSTALLPATH=%MYDIR%..\build - -IF NOT EXIST "%MYDIR%..\support\windows\cmake\build.cmd" ( - ECHO "support git submodule was not checked out" - GOTO exit -) - -IF NOT EXIST "%MYDIR%..\src\platform\windows\build.cmd" ( - ECHO "platform git submodule was not checked out" - GOTO exit -) - -rmdir /s /q "%MYDIR%..\build" 2> nul - -FOR %%T IN (amd64 x86) DO ( - echo Run "%MYDIR%build-lib.cmd" %%T - CALL "%MYDIR%build-lib.cmd" %%T %BUILDTYPE% %VSVERSION% "%INSTALLPATH%" nmake - IF NOT ERRORLEVEL 0 ( - GOTO builderror - ) -) - -rmdir /s /q "%MYDIR%..\build\cmake" 2> nul -exit /b 0 - -:builderror -echo "Failed to build" - -:exit -exit /b 1 diff --git a/windows/create-installer.cmd b/windows/create-installer.cmd index d08c08f3..43a62656 100644 --- a/windows/create-installer.cmd +++ b/windows/create-installer.cmd @@ -1,158 +1,114 @@ -@echo off +@ECHO OFF -set EXITCODE=1 -SET MYDIR=%~dp0 - -rem Check for support folder -IF NOT EXIST "%MYDIR%..\support\windows\p8-usbcec-driver-installer.exe" ( - echo "support submodule was not checked out" - goto RETURNEXIT -) +rem Build a libCEC installer for Windows +rem Usage: create-installer.cmd [visual studio version] [build type] -rem Check for NSIS -IF EXIST "%ProgramFiles%\NSIS\makensis.exe" ( - set NSIS="%ProgramFiles%\NSIS\makensis.exe" -) ELSE IF EXIST "%ProgramFiles(x86)%\NSIS\makensis.exe" ( - set NSIS="%ProgramFiles(x86)%\NSIS\makensis.exe" -) ELSE GOTO NONSIS +SETLOCAL -rem Check for VC14 -IF "%VS140COMNTOOLS%"=="" ( - set COMPILER14="%ProgramFiles%\Microsoft Visual Studio 14.0\Common7\IDE\devenv.com" -) ELSE IF EXIST "%VS140COMNTOOLS%\..\IDE\VCExpress.exe" ( - set COMPILER14="%VS140COMNTOOLS%\..\IDE\VCExpress.exe" -) ELSE IF EXIST "%VS140COMNTOOLS%\..\IDE\devenv.com" ( - set COMPILER14="%VS140COMNTOOLS%\..\IDE\devenv.com" -) ELSE GOTO NOSDK14 +SET MYDIR=%~dp0 -rmdir /s /q %MYDIR%..\build 2> nul -call %MYDIR%build.cmd -IF NOT ERRORLEVEL 0 ( - GOTO ERRORCREATINGINSTALLER +rem optional parameter: visual studio version (2019) +IF "%1" == "" ( + SET VSVERSION=2019 +) ELSE ( + SET VSVERSION=%1 +) +rem optional parameter: build type (Release) +IF "%2" == "" ( + SET BUILDTYPE=Release +) ELSE ( + SET BUILDTYPE=%2 ) -copy "%MYDIR%..\support\windows\p8-usbcec-driver-installer.exe" "%MYDIR%..\build\." -cd "%MYDIR%..\project" - -rem Skip to libCEC/x86 when we're running on win32 -if "%PROCESSOR_ARCHITECTURE%"=="x86" if "%PROCESSOR_ARCHITEW6432%"=="" goto libcecx86 - -rem Compile libCEC and cec-client x64 -echo. Cleaning libCEC (x64) -%COMPILER14% libcec.sln /Clean "Release|x64" -echo. Compiling libCEC (x64) -%COMPILER14% libcec.sln /Build "Release|x64" /Project LibCecSharp -%COMPILER14% libcec.sln /Build "Release|x64" -echo. Compiling .Net applications -cd "%MYDIR%..\src\dotnet\project" -%COMPILER14% cec-dotnet.sln /Build "Release|x64" -copy ..\build\x64\CecSharpTester.exe %MYDIR%..\build\amd64\CecSharpTester.exe -copy ..\build\x64\cec-tray.exe %MYDIR%..\build\amd64\cec-tray.exe - -:libcecx86 -rem Compile libCEC and cec-client Win32 -cd "%MYDIR%..\project" -echo. Cleaning libCEC (x86) -%COMPILER14% libcec.sln /Clean "Release|x86" -echo. Compiling libCEC (x86) -%COMPILER14% libcec.sln /Build "Release|x86" /Project LibCecSharp -%COMPILER14% libcec.sln /Build "Release|x86" -echo. Compiling .Net applications -cd "%MYDIR%..\src\dotnet\project" -%COMPILER14% cec-dotnet.sln /Build "Release|x86" -copy ..\build\x86\CecSharpTester.exe %MYDIR%..\build\x86\CecSharpTester.exe -copy ..\build\x86\cec-tray.exe %MYDIR%..\build\x86\cec-tray.exe -cd "%MYDIR%..\project" - -rem Clean things up before creating the installer -del /q /f %MYDIR%..\build\x86\LibCecSharp.pdb 2> nul -del /q /f %MYDIR%..\build\amd64\LibCecSharp.pdb 2> nul - -GOTO CREATEEGPLUGIN +SET BUILDPATH=%MYDIR%..\build +SET EXITCODE=1 -:SIGNBINARIES -rem Check for sign-binary.cmd, only present on the Pulse-Eight production build system -rem Calls signtool.exe and signs the DLLs with Pulse-Eight's code signing key -IF NOT EXIST "..\support\private\sign-binary.cmd" GOTO CREATEINSTALLER -echo. Signing all binaries -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\x86\cec.dll -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\x86\LibCecSharp.dll -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\x86\cec-client.exe -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\x86\cecc-client.exe -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\x86\cec-tray.exe -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\x86\CecSharpTester.exe -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\amd64\cec.dll -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\amd64\LibCecSharp.dll -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\amd64\cec-client.exe -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\amd64\cecc-client.exe -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\amd64\cec-tray.exe -CALL ..\support\private\sign-binary.cmd %MYDIR%..\build\amd64\CecSharpTester.exe +rem Delete previous build dirs +ECHO. * clearing old build directories +RMDIR /s /q "%MYDIR%..\build" >nul 2>&1 +RMDIR /s /q "%MYDIR%..\src\dotnet\build" >nul 2>&1 +MKDIR "%MYDIR%..\build" >nul +rem Skip to libCEC/x86 if we're running on win32 +IF "%PROCESSOR_ARCHITECTURE%"=="x86" IF "%PROCESSOR_ARCHITEW6432%"=="" GOTO libcecx86 -:CREATEINSTALLER -echo. Creating the installer -cd %MYDIR%..\build\x86 -copy cec.dll libcec.dll -cd ..\amd64 -copy cec.dll cec.x64.dll -cd %MYDIR%..\project -%NSIS% /V1 /X"SetCompressor /FINAL lzma" "libCEC.nsi" - -FOR /F "delims=" %%F IN ('dir /b /s "%MYDIR%..\build\libCEC-*.exe" 2^>nul') DO SET INSTALLER=%%F -IF [%INSTALLER%] == [] ( - GOTO EGPLUGINCLEANUP - GOTO ERRORCREATINGINSTALLER +:libcecx64 +CALL "%MYDIR%build-all.cmd" amd64 %BUILDTYPE% %VSVERSION% +IF %errorlevel% neq 0 ( + ECHO. *** failed to build libCEC for x64 *** + SET EXITCODE=1 + GOTO EXIT ) -rem Sign the installer if sign-binary.cmd exists -IF EXIST "..\support\private\sign-binary.cmd" ( - echo. Signing the installer binaries - CALL ..\support\private\sign-binary.cmd %INSTALLER% +:libcecx86 +CALL "%MYDIR%build-all.cmd" x86 %BUILDTYPE% %VSVERSION% +IF %errorlevel% neq 0 ( + ECHO. *** failed to build libCEC for x86 *** + SET EXITCODE=1 + GOTO EXIT ) -echo. The installer can be found here: %INSTALLER% -set EXITCODE=0 -del /q /f %EGSOURCES%..\pulse_eight.egplugin 2> nul -GOTO EXIT - :CREATEEGPLUGIN -echo. Creating EventGhost plugin file +ECHO. * creating EventGhost plugin SET EGSOURCES=%MYDIR%..\src\eventghost\egplugin_sources\ -copy %MYDIR%..\build\x86\python\cec\__init__.py %EGSOURCES%PulseEight\cec -copy %MYDIR%..\build\x86\python\cec\_cec.pyd %EGSOURCES%PulseEight -copy %MYDIR%..\build\x86\cec.dll %EGSOURCES%PulseEight -del /q /f %EGSOURCES%..\pulse_eight.egplugin 2> nul +COPY "%MYDIR%..\build\x86\python\cec\__init__.py" "%EGSOURCES%PulseEight\cec" >nul +COPY "%MYDIR%..\build\x86\python\cec\_cec.pyd" "%EGSOURCES%PulseEight" >nul +COPY "%MYDIR%..\build\x86\cec.dll" "%EGSOURCES%PulseEight" >nul +DEL /q /f "%EGSOURCES%..\pulse_eight.egplugin" >nul 2>&1 PowerShell -ExecutionPolicy ByPass -Command "Add-Type -Assembly System.IO.Compression.FileSystem;[System.IO.Compression.ZipFile]::CreateFromDirectory('%EGSOURCES%', '%EGSOURCES%..\pulse_eight.egplugin', [System.IO.Compression.CompressionLevel]::Optimal, $false)" -del /q /f %EGSOURCES%PulseEight\cec\__init__.py 2> nul -del /q /f %EGSOURCES%PulseEight\_cec.pyd 2> nul -del /q /f %EGSOURCES%PulseEight\cec.dll 2> nul +DEL /q /f "%EGSOURCES%PulseEight\cec\__init__.py" >nul 2>&1 +DEL /q /f "%EGSOURCES%PulseEight\_cec.pyd" >nul 2>&1 +DEL /q /f "%EGSOURCES%PulseEight\cec.dll" >nul 2>&1 IF NOT EXIST "%EGSOURCES%..\pulse_eight.egplugin" ( - GOTO NOEGPLUGIN + ECHO. *** failed to create EventGhost plugin *** + SET EXITCODE=1 + GOTO EXIT ) -GOTO SIGNBINARIES -:NOEGPLUGIN -echo. Failed to create the EventGhost plugin file. -GOTO EXIT - -:NOSDK14 -echo. Visual Studio 2015 was not found on your system. -GOTO EXIT - -:NOSIS -echo. NSIS could not be found on your system. -GOTO EXIT +:SIGNBINARIES +rem Check for sign-binary.cmd, only present on the Pulse-Eight production build system +rem Calls signtool.exe and signs the DLLs with Pulse-Eight's code signing key +IF NOT EXIST "..\support\private\sign-binary.cmd" GOTO CREATEINSTALLER +ECHO. * signing all binaries +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\x86\cec.dll" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\x86\LibCecSharp.dll" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\x86\cec-tray.exe" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\x86\CecSharpTester.exe" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\x86\netcore\LibCecSharpCore.dll" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\x86\netcore\CecSharpCoreTester.dll" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\x86\netcore\CecSharpCoreTester.exe" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\x86\cec-client.exe" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\x86\cecc-client.exe" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\amd64\cec.dll" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\amd64\LibCecSharp.dll" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\amd64\cec-tray.exe" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\amd64\CecSharpTester.exe" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\amd64\netcore\LibCecSharpCore.dll" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\amd64\netcore\CecSharpCoreTester.dll" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\amd64\netcore\CecSharpCoreTester.exe" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\amd64\cec-client.exe" >nul +CALL ..\support\private\sign-binary.cmd "%MYDIR%..\build\amd64\cecc-client.exe" >nul -:NODDK -echo. Windows DDK could not be found on your system -GOTO EXIT +:CREATEINSTALLER +rem Copy prebuilt driver +COPY "%MYDIR%..\support\windows\p8-usbcec-driver-installer.exe" "%MYDIR%..\build\." >nul +RMDIR /s /q "%MYDIR%..\build\ref" >nul 2>&1 + +CALL "%MYDIR%nsis-helper.cmd" libcec.nsi "libcec-*.exe" +IF %errorlevel% neq 0 ( + ECHO. *** failed to build installer *** + SET EXITCODE=%errorlevel% + GOTO EXIT +) -:ERRORCREATINGINSTALLER -echo. The installer could not be created. The most likely cause is that something went wrong while compiling. +CALL "%MYDIR%nsis-helper.cmd" libcec.nsi "libcec-dbg-*.exe" "/DNSISINCLUDEPDB" +IF %errorlevel% neq 0 ( + ECHO. *** failed to build installer *** + SET EXITCODE=%errorlevel% + GOTO EXIT +) :EXIT -cd %MYDIR%.. - -:RETURNEXIT +CD "%MYDIR%" +PAUSE exit /b %EXITCODE% -pause \ No newline at end of file diff --git a/windows/nsis-helper.cmd b/windows/nsis-helper.cmd new file mode 100644 index 00000000..a6932845 --- /dev/null +++ b/windows/nsis-helper.cmd @@ -0,0 +1,56 @@ +@ECHO OFF + +rem NSIS helper script. Used by create-installer.cmd + +SETLOCAL + +SET MYDIR=%~dp0 + +IF [%2] == [] ( + ECHO.This script should not be called manually. + PAUSE + EXIT /b 99 +) + +rem Check for NSIS +IF EXIST "%ProgramFiles%\NSIS\makensis.exe" ( + SET NSIS="%ProgramFiles%\NSIS\makensis.exe" +) ELSE IF EXIST "%ProgramFiles(x86)%\NSIS\makensis.exe" ( + SET NSIS="%ProgramFiles(x86)%\NSIS\makensis.exe" +) ELSE ( + ECHO.*** NSIS could not be found *** + ECHO. + ECHO.See docs\README.windows.md + EXIT /b 2 +) + +SET NSISPROJECT="%1" +SET RESULTMATCH=%2 +SET RESULTMATCH=%RESULTMATCH:"=% +IF [%3] == [] ( + SET NSISOPTS="" +) ELSE ( + SET NSISOPTS=%3 +) +SET NSISOPTS=%NSISOPTS:"=% + +ECHO. * creating installer "%1" +CD "%MYDIR%..\project" +DEL /F /Q "%MYDIR%..\build\%RESULTMATCH%" >nul 2>&1 +%NSIS% /V1 %NSISOPTS% %NSISPROJECT% + +FOR /F "delims=" %%F IN ('dir /b /s "%MYDIR%..\build\%RESULTMATCH%" 2^>nul') DO SET INSTALLER=%%F +IF [%INSTALLER%] == [] ( + ECHO. *** the installer could not be created *** + ECHO. + ECHO. The most likely cause is that something went wrong while compiling. + EXIT /B 3 +) + +rem Sign the installer if sign-binary.cmd exists +IF EXIST "..\support\private\sign-binary.cmd" ( + ECHO. * signing installer binary + CALL ..\support\private\sign-binary.cmd "%INSTALLER%" >nul +) + +ECHO.installer built: %INSTALLER% diff --git a/windows/visual-studio.cmd b/windows/visual-studio.cmd index efe3d9cd..a69265d4 100644 --- a/windows/visual-studio.cmd +++ b/windows/visual-studio.cmd @@ -1,34 +1,32 @@ @ECHO OFF rem Generate Visual Studio projects for libCEC +rem Usage: build-all.cmd [visual studio version] SETLOCAL +rem optional parameter: visual studio version (2019) +IF "%1" == "" ( + SET VSVERSION=2019 +) ELSE ( + SET VSVERSION=%1 +) + SET MYDIR=%~dp0 SET BUILDTYPE=Debug -SET VSVERSION=14 SET INSTALLPATH=%MYDIR%..\build -IF NOT EXIST "%MYDIR%..\support\windows\cmake\build.cmd" ( - ECHO "support git submodule was not checked out" - GOTO exit -) - -IF NOT EXIST "%MYDIR%..\src\platform\windows\build.cmd" ( - ECHO "platform git submodule was not checked out" - GOTO exit -) - -del /s /f /q %MYDIR%..\build +rem delete old build folder +RMDIR /s /q "%MYDIR%..\build" >nul 2>&1 +rem build/generate vs project files FOR %%T IN (amd64 x86) DO ( - CALL %MYDIR%build-lib.cmd %%T %BUILDTYPE% %VSVERSION% %INSTALLPATH% vs + CALL "%MYDIR%build-lib.cmd" %%T %BUILDTYPE% %VSVERSION% "%INSTALLPATH%" vs + IF %errorlevel% neq 0 EXIT /b %errorlevel% ) ECHO Visual Studio solutions can be found in: -ECHO 32 bits: %MYDIR%..\build\cmake\x86\libcec.sln -ECHO 64 bits: %MYDIR%..\build\cmake\amd64\libcec.sln +ECHO 32 bits: "%MYDIR%..\build\cmake\x86\libcec.sln" +ECHO 64 bits: "%MYDIR%..\build\cmake\amd64\libcec.sln" ECHO. -ECHO These projects only compile in %BUILDTYPE% mode - -:exit +ECHO These projects only compile in %BUILDTYPE% mode and have been generated for Visual Studio %VSVERSION%. From 969ead2c3ac8f8a648cb9af1c05a5c5f84d401fd Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Sat, 25 Apr 2020 10:33:43 +0200 Subject: [PATCH 87/93] fixed: missing LibCecSharpCore project update in the last commit --- .gitignore | 1 - src/dotnetlib/LibCecSharpCore/project/app.ico | Bin 370070 -> 0 bytes src/dotnetlib/LibCecSharpCore/project/app.rc | Bin 0 -> 2118 bytes 3 files changed, 1 deletion(-) delete mode 100644 src/dotnetlib/LibCecSharpCore/project/app.ico create mode 100644 src/dotnetlib/LibCecSharpCore/project/app.rc diff --git a/.gitignore b/.gitignore index 9fd96d21..8c3b8d7a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,6 @@ env.h version.h .vs -*.rc AssemblyInfo.cs AssemblyInfo.cpp diff --git a/src/dotnetlib/LibCecSharpCore/project/app.ico b/src/dotnetlib/LibCecSharpCore/project/app.ico deleted file mode 100644 index dc1baae96a830e8f8a23b432e73dc6201073d1d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 370070 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 RS->n{7BCB#1^T!J{vW0JHckKl diff --git a/src/dotnetlib/LibCecSharpCore/project/app.rc b/src/dotnetlib/LibCecSharpCore/project/app.rc new file mode 100644 index 0000000000000000000000000000000000000000..b37dcbf13dfe9f4d1473204004a8ece35a5b6638 GIT binary patch literal 2118 zcmds3T}#6-6g|&^{~>fQim>|e3*1&@xT!cD3ImbNxHHO8E>K4lqlJR;$!i31jcXFMTgol!n#JECmHR_k*1 zhU^=TWNm_1>eR7|9qtm^wnk~n*@W67s~Z&7*D*$5wY9Ql^4va9KSN`bAFv+*)paMxd|~VN3Km?^vQ*D?%VnUOb|w%uZB#IaL2Ds(IlV6`)D~Y zJ=G(kv{}<|Mj8#SI%yeSt1jC+i?MZ{eu z2r%GyK!&)i@Uu@RTl-XY${O#bZl{>$_jg<_=A$Ctgj4mhj`v93eXWX1Y)@O3i;5@o zwyM{9?0su^3yN=8OomPI!r0FF`skf0 Date: Sat, 25 Apr 2020 10:58:23 +0200 Subject: [PATCH 88/93] changed: default to vs2019 --- src/platform | 2 +- support | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform b/src/platform index 61976431..1c9d14fa 160000 --- a/src/platform +++ b/src/platform @@ -1 +1 @@ -Subproject commit 6197643150d1cebc4d686300193402bc979618ab +Subproject commit 1c9d14fa996af33760a2c700caebd2bd9ae527c9 diff --git a/support b/support index d0a6b566..7c412111 160000 --- a/support +++ b/support @@ -1 +1 @@ -Subproject commit d0a6b566f9723fbab971cbcc17696f6da06bf879 +Subproject commit 7c4121112af37d641ea02383d53b50a38087115a From 994b437f79e69bb39a917b84d635b3211586e5c7 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Sat, 25 Apr 2020 11:00:32 +0200 Subject: [PATCH 89/93] fixed: restore nuget packages when doing a clean build --- windows/build-all.cmd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/windows/build-all.cmd b/windows/build-all.cmd index d39e3dca..8e244fb5 100644 --- a/windows/build-all.cmd +++ b/windows/build-all.cmd @@ -96,6 +96,8 @@ IF EXIST "%MYDIR%..\build\%BUILDARCH%\netcore\LibCecSharpCore.dll" ( rem Compile cec-tray and CecSharpTester apps ECHO. * compiling .Net applications for %BUILDARCH% CD "%MYDIR%..\src\dotnet\project" +rem Restore nuget dependencies +msbuild -t:restore "%DevEnvDir%devenv.com" cec-dotnet.sln /Build "%BUILDTYPE%|%BUILDARCHPROJECT%" rem Check and copy CecSharpTester From d1affd7a257983d871bf6b70c03148e20f9823eb Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Sat, 25 Apr 2020 11:19:17 +0200 Subject: [PATCH 90/93] fixed: CMP0086 got added in cmake 3.14 --- src/libcec/cmake/CheckPlatformSupport.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index 755d9cf3..1d99bd87 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -222,6 +222,9 @@ else() if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") # old style swig cmake_policy(SET CMP0078 OLD) + endif() + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14") + # old style swig cmake_policy(SET CMP0086 OLD) endif() From 88894aa1c5f2dfe5c9660eb951e181a26918cafd Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Mon, 27 Apr 2020 11:35:01 +0200 Subject: [PATCH 91/93] fixed: 15 and 16 byte frames didn't fit and couldn't be sent closes #443 --- .../Pulse-Eight/USBCECAdapterCommands.cpp | 10 ++- .../USBCECAdapterCommunication.cpp | 2 +- .../Pulse-Eight/USBCECAdapterMessage.cpp | 73 +++++++++---------- .../Pulse-Eight/USBCECAdapterMessage.h | 20 ++--- .../Pulse-Eight/USBCECAdapterMessageQueue.cpp | 30 +++----- 5 files changed, 58 insertions(+), 77 deletions(-) diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp index a2a6ad67..b5aaadcc 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp @@ -69,11 +69,13 @@ cec_datapacket CUSBCECAdapterCommands::RequestSetting(cec_adapter_messagecode ms CCECAdapterMessage params; CCECAdapterMessage *message = m_comm->SendCommand(msgCode, params); - if (message && message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED) + if (!!message && + (message->state == ADAPTER_MESSAGE_STATE_SENT_ACKED) && + (message->m_rx_len >= 3)) { - retVal = message->response; - retVal.Shift(2); // shift out start and msgcode - retVal.size -= 1; // remove end + // shift out start, msgcode and end + memcpy(retVal.data, &message->m_rx_data[2], message->m_rx_len - 3); + retVal.size = message->m_rx_len - 3; } SAFE_DELETE(message); diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp index 80a1e41e..b048cb8b 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp @@ -380,7 +380,7 @@ bool CUSBCECAdapterCommunication::WriteToDevice(CCECAdapterMessage *message) } /* write the message */ - if (m_port->Write(message->packet.data, message->Size()) != (ssize_t) message->Size()) + if (m_port->Write(message->m_tx_data, message->m_tx_len) != (ssize_t)message->m_tx_len) { LIB_CEC->AddLog(CEC_LOG_DEBUG, "error writing command '%s' to serial port '%s': %s", CCECAdapterMessage::ToString(message->Message()), m_port->GetName().c_str(), m_port->GetError().c_str()); message->state = ADAPTER_MESSAGE_STATE_ERROR; diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.cpp index 9ddb61d7..64bb59c8 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.cpp @@ -244,54 +244,48 @@ const char *CCECAdapterMessage::ToString(cec_adapter_messagecode msgCode) uint8_t CCECAdapterMessage::operator[](uint8_t pos) const { - return pos < packet.size ? packet[pos] : 0; + return (pos < m_tx_len) ? m_tx_data[pos] : 0; } uint8_t CCECAdapterMessage::At(uint8_t pos) const { - return pos < packet.size ? packet[pos] : 0; + return (pos < m_tx_len) ? m_tx_data[pos] : 0; } uint8_t CCECAdapterMessage::Size(void) const { - return packet.size; + return m_tx_len; } bool CCECAdapterMessage::IsEmpty(void) const { - return packet.IsEmpty(); + return (m_tx_len == 0); } void CCECAdapterMessage::Clear(void) { - state = ADAPTER_MESSAGE_STATE_UNKNOWN; - transmit_timeout = CEC_DEFAULT_TRANSMIT_TIMEOUT; - response.Clear(); - packet.Clear(); - lineTimeout = 3; - bNextByteIsEscaped = false; - bFireAndForget = false; -} - -void CCECAdapterMessage::Shift(uint8_t iShiftBy) -{ - packet.Shift(iShiftBy); + state = ADAPTER_MESSAGE_STATE_UNKNOWN; + transmit_timeout = CEC_DEFAULT_TRANSMIT_TIMEOUT; + lineTimeout = 3; + bNextByteIsEscaped = false; + bFireAndForget = false; + m_tx_len = 0; + m_rx_len = 0; } void CCECAdapterMessage::Append(CCECAdapterMessage &data) { - Append(data.packet); -} - -void CCECAdapterMessage::Append(cec_datapacket &data) -{ - for (uint8_t iPtr = 0; iPtr < data.size; iPtr++) - PushBack(data[iPtr]); + uint8_t len = data.m_tx_len; + if (len + m_tx_len > USBCEC_MAX_MSG_SIZE) + len = USBCEC_MAX_MSG_SIZE - m_tx_len; + memcpy(&m_tx_data[m_tx_len], data.m_tx_data, len); + m_tx_len += len; } void CCECAdapterMessage::PushBack(uint8_t byte) { - packet.PushBack(byte); + if (m_tx_len < USBCEC_MAX_MSG_SIZE) + m_tx_data[m_tx_len++] = byte; } void CCECAdapterMessage::PushEscaped(uint8_t byte) @@ -336,15 +330,15 @@ bool CCECAdapterMessage::PushReceivedByte(uint8_t byte) cec_adapter_messagecode CCECAdapterMessage::Message(void) const { - return packet.size >= 2 ? - (cec_adapter_messagecode) (packet.At(1) & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) : + return (m_tx_len >= 2) ? + (cec_adapter_messagecode) (m_tx_data[1] & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) : MSGCODE_NOTHING; } cec_adapter_messagecode CCECAdapterMessage::ResponseTo(void) const { - return packet.size >= 3 ? - (cec_adapter_messagecode) (packet.At(2) & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) : + return (m_tx_len >= 3) ? + (cec_adapter_messagecode) (m_tx_data[2] & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) : MSGCODE_NOTHING; } @@ -370,15 +364,15 @@ bool CCECAdapterMessage::IsTransmission(void) const bool CCECAdapterMessage::IsEOM(void) const { - return packet.size >= 2 ? - (packet.At(1) & MSGCODE_FRAME_EOM) != 0 : + return (m_tx_len >= 2) ? + (m_tx_data[1] & MSGCODE_FRAME_EOM) != 0 : false; } bool CCECAdapterMessage::IsACK(void) const { - return packet.size >= 2 ? - (packet.At(1) & MSGCODE_FRAME_ACK) != 0 : + return (m_tx_len >= 2) ? + (m_tx_data[1] & MSGCODE_FRAME_ACK) != 0 : false; } @@ -418,21 +412,22 @@ bool CCECAdapterMessage::NeedsRetry(void) const cec_logical_address CCECAdapterMessage::Initiator(void) const { - return packet.size >= 3 ? - (cec_logical_address) (packet.At(2) >> 4) : + return (m_tx_len >= 3) ? + (cec_logical_address) (m_tx_data[2] >> 4) : CECDEVICE_UNKNOWN; } cec_logical_address CCECAdapterMessage::Destination(void) const { - return packet.size >= 3 ? - (cec_logical_address) (packet.At(2) & 0xF) : + return (m_tx_len >= 3) ? + (cec_logical_address) (m_tx_data[2] & 0xF) : CECDEVICE_UNKNOWN; } bool CCECAdapterMessage::HasStartMessage(void) const { - return packet.size >= 1 && packet.At(0) == MSGSTART; + return (m_tx_len >= 1) && + (m_tx_data[0] == MSGSTART); } bool CCECAdapterMessage::PushToCecCommand(cec_command &command) const @@ -469,7 +464,7 @@ bool CCECAdapterMessage::PushToCecCommand(cec_command &command) const cec_adapter_messagecode CCECAdapterMessage::Reply(void) const { - return response.size >= 2 ? - (cec_adapter_messagecode) (response.At(1) & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) : + return (m_rx_len >= 2) ? + (cec_adapter_messagecode) (m_rx_data[1] & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) : MSGCODE_NOTHING; } diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.h index 756b4610..fefe056d 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.h +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.h @@ -95,6 +95,8 @@ namespace CEC P8_ADAPTERTYPE_DAUGHTERBOARD, } p8_cec_adapter_type; + #define USBCEC_MAX_MSG_SIZE (18 * 4) + class CCECAdapterMessage { public: @@ -145,24 +147,12 @@ namespace CEC */ void Clear(void); - /*! - * @brief Shift the message by the given number of bytes. - * @param iShiftBy The number of bytes to shift. - */ - void Shift(uint8_t iShiftBy); - /*! * @brief Append the given message to this message. * @param data The message to append. */ void Append(CCECAdapterMessage &data); - /*! - * @brief Append the given datapacket to this message. - * @param data The packet to add. - */ - void Append(cec_datapacket &data); - /*! * @brief Adds a byte to this message. Does not escape the byte. * @param byte The byte to add. @@ -256,8 +246,10 @@ namespace CEC */ cec_adapter_messagecode Reply(void) const; - cec_datapacket response; /**< the response to this message */ - cec_datapacket packet; /**< the actual data */ + uint8_t m_tx_data[USBCEC_MAX_MSG_SIZE]; + uint8_t m_tx_len; + uint8_t m_rx_data[USBCEC_MAX_MSG_SIZE]; + uint8_t m_rx_len; cec_adapter_message_state state; /**< the current state of this message */ int32_t transmit_timeout; /**< the timeout to use when sending this message */ uint8_t lineTimeout; /**< the default CEC line timeout to use when sending this message */ diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp index 80a36619..064cc120 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessageQueue.cpp @@ -214,7 +214,8 @@ bool CCECAdapterMessageQueueEntry::MessageReceivedCommandAccepted(const CCECAdap if (!m_message->IsTransmission() && m_iPacketsLeft == 0) { m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED; - m_message->response = message.packet; + memcpy(m_message->m_rx_data, message.m_tx_data, message.m_tx_len); + m_message->m_rx_len = message.m_tx_len; bSendSignal = true; } bHandled = true; @@ -238,7 +239,8 @@ bool CCECAdapterMessageQueueEntry::MessageReceivedTransmitSucceeded(const CCECAd m_queue->m_com->m_callback->GetLib()->AddLog(CEC_LOG_DEBUG, "%s - transmit succeeded", m_message->ToString().c_str()); #endif m_message->state = ADAPTER_MESSAGE_STATE_SENT_ACKED; - m_message->response = message.packet; + memcpy(m_message->m_rx_data, message.m_tx_data, message.m_tx_len); + m_message->m_rx_len = message.m_tx_len; m_queue->m_com->OnTxAck(); } else @@ -269,7 +271,8 @@ bool CCECAdapterMessageQueueEntry::MessageReceivedResponse(const CCECAdapterMess m_queue->m_com->OnTxError(); } #endif - m_message->response = message.packet; + memcpy(m_message->m_rx_data, message.m_tx_data, message.m_tx_len); + m_message->m_rx_len = message.m_tx_len; if (m_message->IsTransmission()) { if (message.Message() == MSGCODE_TRANSMIT_SUCCEEDED) @@ -417,25 +420,14 @@ void CCECAdapterMessageQueue::MessageReceived(const CCECAdapterMessage &msg) } } -void CCECAdapterMessageQueue::AddData(uint8_t *data, size_t iLen) +void CCECAdapterMessageQueue::AddData(uint8_t *data, size_t len) { - for (size_t iPtr = 0; iPtr < iLen; iPtr++) + for (size_t ptr = 0; ptr < len; ++ptr) { - bool bFullMessage(false); + if (m_incomingAdapterMessage->PushReceivedByte(data[ptr])) { - CLockObject lock(m_mutex); - bFullMessage = m_incomingAdapterMessage->PushReceivedByte(data[iPtr]); - } - - if (bFullMessage) - { - /* a full message was received */ - CCECAdapterMessage newMessage; - newMessage.packet = m_incomingAdapterMessage->packet; - MessageReceived(newMessage); - - /* clear the current message */ - CLockObject lock(m_mutex); + // a full message was received + MessageReceived(*m_incomingAdapterMessage); m_incomingAdapterMessage->Clear(); } } From b6f8e6932c27abe1a7f0042588eda4be94044405 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Mon, 27 Apr 2020 12:21:50 +0200 Subject: [PATCH 92/93] fixed: osd name truncated (after v5 bump) closes #333 --- include/cectypes.h | 12 +++++++++--- src/cec-client/cec-client.cpp | 4 ++-- src/libcec/CECClient.cpp | 6 +++--- src/libcec/CECProcessor.cpp | 2 +- src/libcec/LibCEC.cpp | 4 ++-- src/libcec/LibCECC.cpp | 5 ++++- .../adapter/Pulse-Eight/USBCECAdapterCommands.cpp | 10 +++++----- src/libcec/implementations/CECCommandHandler.cpp | 2 +- 8 files changed, 27 insertions(+), 18 deletions(-) diff --git a/include/cectypes.h b/include/cectypes.h index c97d8ba9..35e44ea4 100644 --- a/include/cectypes.h +++ b/include/cectypes.h @@ -1466,10 +1466,16 @@ typedef struct ICECCallbacks #endif } ICECCallbacks; +#if CEC_LIB_VERSION_MAJOR >= 5 +#define LIBCEC_OSD_NAME_SIZE (15) +#else +#define LIBCEC_OSD_NAME_SIZE (13) +#endif + struct libcec_configuration { uint32_t clientVersion; /*!< the version of the client that is connecting */ - char strDeviceName[13]; /*!< the device name to use on the CEC bus */ + char strDeviceName[LIBCEC_OSD_NAME_SIZE]; /*!< the device name to use on the CEC bus, name + 0 terminator */ cec_device_type_list deviceTypes; /*!< the device type(s) to use on the CEC bus for libCEC */ uint8_t bAutodetectAddress; /*!< (read only) set to 1 by libCEC when the physical address was autodetected */ uint16_t iPhysicalAddress; /*!< the physical address of the CEC adapter */ @@ -1513,7 +1519,7 @@ struct libcec_configuration bool operator==(const libcec_configuration &other) const { return ( clientVersion == other.clientVersion && - !strncmp(strDeviceName, other.strDeviceName, 13) && + !strcmp(strDeviceName, other.strDeviceName) && deviceTypes == other.deviceTypes && bAutodetectAddress == other.bAutodetectAddress && iPhysicalAddress == other.iPhysicalAddress && @@ -1581,7 +1587,7 @@ struct libcec_configuration bAutoPowerOn = 0; #endif - memset(strDeviceName, 0, 13); + strDeviceName[0] = (char)0; deviceTypes.Clear(); logicalAddresses.Clear(); wakeDevices.Clear(); diff --git a/src/cec-client/cec-client.cpp b/src/cec-client/cec-client.cpp index c0755f88..63daf16b 100644 --- a/src/cec-client/cec-client.cpp +++ b/src/cec-client/cec-client.cpp @@ -1180,7 +1180,7 @@ bool ProcessCommandLineArguments(int argc, char *argv[]) { if (argc >= iArgPtr + 2) { - snprintf(g_config.strDeviceName, 13, "%s", argv[iArgPtr + 1]); + snprintf(g_config.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", argv[iArgPtr + 1]); std::cout << "using osd name " << g_config.strDeviceName << std::endl; ++iArgPtr; } @@ -1268,7 +1268,7 @@ int main (int argc, char *argv[]) g_config.Clear(); g_callbacks.Clear(); - snprintf(g_config.strDeviceName, 13, "CECTester"); + snprintf(g_config.strDeviceName, LIBCEC_OSD_NAME_SIZE, "CECTester"); g_config.clientVersion = LIBCEC_VERSION_CURRENT; g_config.bActivateSource = 0; g_callbacks.logMessage = &CecLogMessage; diff --git a/src/libcec/CECClient.cpp b/src/libcec/CECClient.cpp index edbeb2c1..ea72091e 100644 --- a/src/libcec/CECClient.cpp +++ b/src/libcec/CECClient.cpp @@ -847,7 +847,7 @@ bool CCECClient::GetCurrentConfiguration(libcec_configuration &configuration) { CLockObject lock(m_mutex); - snprintf(configuration.strDeviceName, 13, "%s", m_configuration.strDeviceName); + snprintf(configuration.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", m_configuration.strDeviceName); configuration.deviceTypes = m_configuration.deviceTypes; configuration.bAutodetectAddress = m_configuration.bAutodetectAddress; configuration.iPhysicalAddress = m_configuration.iPhysicalAddress; @@ -1274,7 +1274,7 @@ void CCECClient::SetOSDName(const std::string &strDeviceName) { { CLockObject lock(m_mutex); - snprintf(m_configuration.strDeviceName, 13, "%s", strDeviceName.c_str()); + snprintf(m_configuration.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", strDeviceName.c_str()); } LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, strDeviceName.c_str()); @@ -1717,4 +1717,4 @@ bool CCECClient::GetStats(struct cec_adapter_stats* stats) m_processor->GetStats(stats) : false; } -#endif \ No newline at end of file +#endif diff --git a/src/libcec/CECProcessor.cpp b/src/libcec/CECProcessor.cpp index 9e7734e7..116c153d 100644 --- a/src/libcec/CECProcessor.cpp +++ b/src/libcec/CECProcessor.cpp @@ -899,7 +899,7 @@ bool CCECProcessor::RegisterClient(CECClientPtr client) configuration.deviceTypes = config.deviceTypes; if (CLibCEC::IsValidPhysicalAddress(config.iPhysicalAddress)) configuration.iPhysicalAddress = config.iPhysicalAddress; - snprintf(configuration.strDeviceName, 13, "%s", config.strDeviceName); + snprintf(configuration.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", config.strDeviceName); } // set the firmware version and build date diff --git a/src/libcec/LibCEC.cpp b/src/libcec/LibCEC.cpp index 8d5e97e7..cf5de495 100644 --- a/src/libcec/LibCEC.cpp +++ b/src/libcec/LibCEC.cpp @@ -455,7 +455,7 @@ void * CECInit(const char *strDeviceName, CEC::cec_device_type_list types) libcec_configuration configuration; configuration.Clear(); // client version < 1.5.0 - snprintf(configuration.strDeviceName, 13, "%s", strDeviceName); + snprintf(configuration.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", strDeviceName); configuration.deviceTypes = types; configuration.iPhysicalAddress = CEC_INVALID_PHYSICAL_ADDRESS; @@ -632,4 +632,4 @@ bool CLibCEC::GetStats(struct cec_adapter_stats* stats) m_client->GetStats(stats) : false; } -#endif \ No newline at end of file +#endif diff --git a/src/libcec/LibCECC.cpp b/src/libcec/LibCECC.cpp index 03244a5c..7dcecdfb 100644 --- a/src/libcec/LibCECC.cpp +++ b/src/libcec/LibCECC.cpp @@ -354,7 +354,10 @@ int libcec_get_device_osd_name(libcec_connection_t connection, cec_logical_addre if (!!adapter) { std::string osdName(adapter->GetDeviceOSDName(iAddress)); - strncpy(name, osdName.c_str(), std::min(sizeof(cec_osd_name), osdName.size())); + size_t osd_size(osdName.size()); + memcpy(name, osdName.c_str(), std::min(sizeof(cec_osd_name), osd_size)); + if (osd_size < sizeof(cec_osd_name)) + name[osd_size] = (char)0; return 0; } return -1; diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp index b5aaadcc..67cc651e 100644 --- a/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp +++ b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp @@ -253,12 +253,12 @@ bool CUSBCECAdapterCommands::RequestSettingOSDName(void) if (response.size == 0) { LIB_CEC->AddLog(CEC_LOG_DEBUG, "no persisted device name setting"); - memset(m_persistedConfiguration.strDeviceName, 0, 13); + m_persistedConfiguration.strDeviceName[0] = (char)0; return false; } - memcpy(m_persistedConfiguration.strDeviceName, response.data, response.size <= 13 ? response.size : 13); - if (response.size < 13) { + memcpy(m_persistedConfiguration.strDeviceName, response.data, response.size <= LIBCEC_OSD_NAME_SIZE ? response.size : LIBCEC_OSD_NAME_SIZE); + if (response.size < LIBCEC_OSD_NAME_SIZE) { m_persistedConfiguration.strDeviceName[response.size] = (char)0; } return true; @@ -511,7 +511,7 @@ bool CUSBCECAdapterCommands::SetSettingOSDName(const char *strOSDName) SAFE_DELETE(message); if (bReturn) - snprintf(m_persistedConfiguration.strDeviceName, 13, "%s", strOSDName); + snprintf(m_persistedConfiguration.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", strOSDName); return bReturn; } @@ -623,7 +623,7 @@ bool CUSBCECAdapterCommands::GetConfiguration(libcec_configuration &configuratio configuration.iFirmwareVersion = m_persistedConfiguration.iFirmwareVersion; configuration.deviceTypes = m_persistedConfiguration.deviceTypes; configuration.iPhysicalAddress = m_persistedConfiguration.iPhysicalAddress; - snprintf(configuration.strDeviceName, 13, "%s", m_persistedConfiguration.strDeviceName); + snprintf(configuration.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", m_persistedConfiguration.strDeviceName); return true; } diff --git a/src/libcec/implementations/CECCommandHandler.cpp b/src/libcec/implementations/CECCommandHandler.cpp index f3bbf4a0..09087f13 100644 --- a/src/libcec/implementations/CECCommandHandler.cpp +++ b/src/libcec/implementations/CECCommandHandler.cpp @@ -616,7 +616,7 @@ int CCECCommandHandler::HandleSetOSDName(const cec_command &command) CCECBusDevice *device = GetDevice(command.initiator); if (device) { - char buf[1024]; + char buf[17]; for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++) buf[iPtr] = (char)command.parameters[iPtr]; buf[command.parameters.size] = 0; From 11677793389efebf4870ca4960bbf11b2d5fa5b9 Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Mon, 27 Apr 2020 13:31:29 +0200 Subject: [PATCH 93/93] bump to 4.0.5 this keeps some new features and changes disabled to prevent API changes --- CMakeLists.txt | 2 +- debian/changelog.in | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a65fe52..4e6c6bb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.12.0) set(LIBCEC_VERSION_MAJOR 4) set(LIBCEC_VERSION_MINOR 0) -set(LIBCEC_VERSION_PATCH 4) +set(LIBCEC_VERSION_PATCH 5) # cec-client add_subdirectory(src/cec-client) diff --git a/debian/changelog.in b/debian/changelog.in index 0d72f2de..14e629c3 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,36 @@ +libcec (4.0.5.1~#DIST#) #DIST#; urgency=medium + + * fixed: + * windows 64-bit detection. issue #473 + * windows build with spaces in path. issue #475 #474 + * USBCECAdapterDetection: Only scan tty. issue #495 + * don't probe all devices in CCECClient::IsActiveDeviceType(). issue #492 + * use cmake TIMESTAMP for reproducible builds. issue #485 #487 + * python 3 client. issue #479 + * add O_CLOEXEC to prevent child process locking. issue #478 #477 + * fix build if hostname does not support '-f' argument. issue #471 + * hyperlinks in README files. issue #465 #455 + * replace SWIG_ADD_MODULE and force python2 on win x86. issue #481 + * cb_cec_log_message() formatting in cecc-client + * missing SetupDiDestroyDeviceInfoList() + * various build script improvements on Windows + * 15 and 16 byte frames didn't fit and couldn't be sent. issue #443 + * added: + * Linux CEC framework adapter. issue #380 + * .Net Core support. issue #130 + * P8 adapter discovery via Linux sysfs. issue #472 + * iMX6 support. issue #323 + * Apple vendor ID. issue #486 + * link to Skull Canyon and Hades Canyon NUC adapter. issue #448 + * Request/Report SAD messages. issue #404 + * changed: + * use hex format for cec-client address input. issue #480 + * adapter detection on windows without advapi + * build using VS2019 on Windows, required for .Net Core + * CMake 3.12+ is now required + + -- Pulse-Eight Packaging Mon, 27 Apr 2020 13:28:00 +0100 + libcec (4.0.4.1~#DIST#) #DIST#; urgency=medium * fixed: only prevent TV polls when a Samsung TV is detected instead of