diff --git a/.gitignore b/.gitignore index 269f7647..8c3b8d7a 100644 --- a/.gitignore +++ b/.gitignore @@ -19,8 +19,8 @@ *.aps env.h version.h +.vs -*.rc AssemblyInfo.cs AssemblyInfo.cpp @@ -73,15 +73,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 +102,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 861ba19a..4e6c6bb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ project(libcec) -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 3.12.0) set(LIBCEC_VERSION_MAJOR 4) set(LIBCEC_VERSION_MINOR 0) -set(LIBCEC_VERSION_PATCH 2) +set(LIBCEC_VERSION_PATCH 5) # cec-client add_subdirectory(src/cec-client) @@ -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/README.md b/README.md index 02373dec..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 @@ -28,3 +29,11 @@ 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. + +## 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` diff --git a/debian/changelog.in b/debian/changelog.in index b1d11c57..14e629c3 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,91 @@ +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 + 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: + * 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: 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/docs/README.developers.md b/docs/README.developers.md index 3aeaf7c7..94449991 100644 --- a/docs/README.developers.md +++ b/docs/README.developers.md @@ -4,22 +4,27 @@ 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 +## .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/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 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. diff --git a/docs/README.linux.md b/docs/README.linux.md index c59fb806..39735c18 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 @@ -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). \ No newline at end of file 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 diff --git a/docs/README.raspberrypi.md b/docs/README.raspberrypi.md index feb6cc17..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 @@ -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) diff --git a/docs/README.windows.md b/docs/README.windows.md index 8375cf85..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/). -### 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/) - -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. +### 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 -### Installation check +An example implementation can be found on [Github](https://github.com/Pulse-Eight/cec-dotnet/tree/master/src/CecSharpTester/netcore/). -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` +### 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 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 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/include/cec.h b/include/cec.h index 71450ae0..607d05a0 100644 --- a/include/cec.h +++ b/include/cec.h @@ -453,6 +453,10 @@ namespace CEC * @return True if the command was sent, false otherwise */ 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 9c918427..35e44ea4 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 */ @@ -291,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 */ @@ -728,77 +748,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; @@ -831,6 +853,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, @@ -861,7 +884,9 @@ typedef enum cec_adapter_type ADAPTERTYPE_RPI = 0x100, ADAPTERTYPE_TDA995x = 0x200, ADAPTERTYPE_EXYNOS = 0x300, - ADAPTERTYPE_AOCEC = 0x500 + ADAPTERTYPE_LINUX = 0x400, + ADAPTERTYPE_AOCEC = 0x500, + ADAPTERTYPE_IMX = 0x600 } cec_adapter_type; /** force exporting through swig */ @@ -1354,6 +1379,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 @@ -1432,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 */ @@ -1468,6 +1508,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(); } @@ -1476,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 && @@ -1501,7 +1544,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 @@ -1536,8 +1583,11 @@ struct libcec_configuration iButtonRepeatRateMs = 0; iButtonReleaseDelayMs = CEC_BUTTON_TIMEOUT; bAutoWakeAVR = 0; +#if CEC_LIB_VERSION_MAJOR >= 5 + bAutoPowerOn = 0; +#endif - memset(strDeviceName, 0, 13); + strDeviceName[0] = (char)0; deviceTypes.Clear(); logicalAddresses.Clear(); wakeDevices.Clear(); 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 9e807bcd..75567e8b 100644 --- a/project/libCEC.nsi +++ b/project/libCEC.nsi @@ -1,36 +1,67 @@ ;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/" -!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 @@ -42,222 +73,24 @@ 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 - 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 3 - - ; 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 3 - - ; 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 3 - - ; 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 3 - - ; Copy to the installation directory - SetOutPath "$INSTDIR\python" - File "..\build\x86\python\pyCecClient.py" - SetOutPath "$INSTDIR\python\cec" - File "..\build\x86\python\cec\__init__.py" - File "..\build\x86\python\cec\_cec.pyd" -SectionEnd - -!define EVENTGHOST_SECTIONNAME "EventGhost plugin" -Section "" SecEvGhostCec - SetShellVarContext current - SectionIn 1 3 - - ${If} $EventGhostLocation != "" - SetOutPath "$EventGhostLocation\plugins\libCEC\cec" - File "..\build\x86\cec.dll" - File "..\build\x86\python\cec\__init__.py" - File "..\build\x86\python\cec\_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 - -!define REDISTRIBUTABLE_X86_SECTIONNAME "Microsoft Visual C++ 2013 Redistributable Package (x86)" -Section "" SecVCRedistX86 - SetShellVarContext current - SectionIn 1 3 - - SetOutPath "$TEMP\vc2013_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 - ${Endif} - - RMDIR /r "$TEMP\vc2013_x86" -SectionEnd - -!define REDISTRIBUTABLE_X64_SECTIONNAME "Microsoft Visual C++ 2013 Redistributable Package (x64)" -Section "" SecVCRedistX64 - SetShellVarContext current - SectionIn 1 3 - - SetOutPath "$TEMP\vc2013_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 - ${Endif} - - RMDIR /r "$TEMP\vc2013_x64" -SectionEnd +; installer sections, separate file to declutter a bit +!include "nsis\sections.nsh" Function .onInit - ; check for vc2013 x86 redist - ReadRegDword $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{f65db027-aff3-4070-886a-0d87064aabb1}" "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} @@ -271,8 +104,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 vc x64 redist + ReadRegDword $1 HKLM "SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Version" ${If} $1 != "" StrCpy $VSRedistInstalledX64 "Yes" ${Endif} @@ -297,70 +130,20 @@ Function .onInit SectionSetText ${SecEvGhostCec} "${EVENTGHOST_SECTIONNAME}" ${Else} !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/" ${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 - ReadRegDword $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\EventGhost_is1" "InstallLocation" - ${If} $1 != "" - RMDir /r "$1\plugins\libCEC" - Delete "$1\libCEC_Demo_Configuration.xml" + 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/EventGhost/__init__.py b/src/EventGhost/__init__.py deleted file mode 100644 index 146ad168..00000000 --- a/src/EventGhost/__init__.py +++ /dev/null @@ -1,331 +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): - 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 __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}\'))'), -) - -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/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/cec.png b/src/EventGhost/egplugin_sources/PulseEight/cec.png similarity index 100% rename from src/EventGhost/cec.png rename to src/EventGhost/egplugin_sources/PulseEight/cec.png diff --git a/src/EventGhost/egplugin_sources/PulseEight/cec_classes.py b/src/EventGhost/egplugin_sources/PulseEight/cec_classes.py new file mode 100644 index 00000000..63b60752 --- /dev/null +++ b/src/EventGhost/egplugin_sources/PulseEight/cec_classes.py @@ -0,0 +1,656 @@ +# -*- 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 . + +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/src/EventGhost/libCEC_Demo_Configuration.xml b/src/EventGhost/libCEC_Demo_Configuration.xml deleted file mode 100644 index 875634e6..00000000 --- a/src/EventGhost/libCEC_Demo_Configuration.xml +++ /dev/null @@ -1,245 +0,0 @@ - - - - - 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) - - - - - diff --git a/src/cec-client/CMakeLists.txt b/src/cec-client/CMakeLists.txt index 2cb42bb3..5418ef7b 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") @@ -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,6 +48,9 @@ if (NOT WIN32) # curses if (HAVE_CURSES_API) target_link_libraries(cec-client curses) + if (HAVE_CURSES_TINFO) + target_link_libraries(cec-client tinfo) + endif() endif() # rt @@ -58,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/cec-client/cec-client.cpp b/src/cec-client/cec-client.cpp index d654b308..63daf16b 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 @@ -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; @@ -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; } } @@ -662,7 +673,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; @@ -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")); } } @@ -931,6 +921,32 @@ 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") + { + 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; +} +#endif + bool ProcessConsoleCommand(ICECAdapter *parser, std::string &input) { if (!input.empty()) @@ -971,7 +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); + ProcessCommandSELF(parser, command, input) +#if CEC_LIB_VERSION_MAJOR >= 5 + || ProcessCommandSTATS(parser, command, input) +#endif + ; } } return true; @@ -1160,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; } @@ -1199,6 +1219,28 @@ bool ProcessCommandLineArguments(int argc, char *argv[]) ++iArgPtr; } } +#endif +#if CEC_LIB_VERSION_MAJOR >= 5 + 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; + } #endif else { @@ -1213,11 +1255,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[]) @@ -1230,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; @@ -1354,7 +1392,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 +1405,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/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/CMakeLists.txt b/src/cecc-client/CMakeLists.txt index 3b8b9e86..80f164bc 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") @@ -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/cecc-client/cecc-client.c b/src/cecc-client/cecc-client.c index 33731095..d65b77fc 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) { @@ -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); } } 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/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 99% rename from src/LibCecSharp/CecSharpTypes.h rename to src/dotnetlib/CecSharpTypes.h index d73f4f27..8af13278 100644 --- a/src/LibCecSharp/CecSharpTypes.h +++ b/src/dotnetlib/CecSharpTypes.h @@ -31,7 +31,7 @@ * http://www.pulse-eight.net/ */ -#include +#include "p8-platform/threads/mutex.h" #include #include #include "../../include/cec.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/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.rc b/src/dotnetlib/LibCecSharpCore/project/app.rc new file mode 100644 index 00000000..b37dcbf1 Binary files /dev/null and b/src/dotnetlib/LibCecSharpCore/project/app.rc differ 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/CECClient.cpp b/src/libcec/CECClient.cpp index b777faae..ea72091e 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 @@ -143,6 +143,8 @@ bool CCECClient::OnRegister(void) if (m_configuration.bActivateSource == 1) GetPrimaryDevice()->ActivateSource(500); + PersistConfiguration(m_configuration); + return true; } @@ -845,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; @@ -912,6 +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); @@ -1269,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()); @@ -1457,11 +1462,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, false); } cec_logical_address CCECClient::GetActiveSource(void) @@ -1708,3 +1709,12 @@ bool CCECClient::AudioEnable(bool enable) audio->EnableAudio(device) : false; } + +#if CEC_LIB_VERSION_MAJOR >= 5 +bool CCECClient::GetStats(struct cec_adapter_stats* stats) +{ + return !!m_processor ? + m_processor->GetStats(stats) : + false; +} +#endif diff --git a/src/libcec/CECClient.h b/src/libcec/CECClient.h index 49528eef..ff71e848 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 @@ -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/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..116c153d 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; @@ -333,6 +333,15 @@ 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) { if (m_communication) @@ -683,6 +692,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++) @@ -883,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/CECProcessor.h b/src/libcec/CECProcessor.h index 08917b93..8b4f0e0a 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" @@ -127,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); @@ -134,6 +133,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/CECTypeUtils.h b/src/libcec/CECTypeUtils.h index 0d0cf178..60f35efb 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 { @@ -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: @@ -766,6 +768,10 @@ namespace CEC return "Raspberry Pi"; case ADAPTERTYPE_TDA995x: return "TDA995x"; + case ADAPTERTYPE_IMX: + return "i.MX"; + case ADAPTERTYPE_LINUX: + return "Linux"; default: return "unknown"; } diff --git a/src/libcec/CMakeLists.txt b/src/libcec/CMakeLists.txt index 5c888070..19a070b7 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") @@ -75,6 +74,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}) @@ -88,6 +88,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 @@ -104,6 +106,8 @@ set(CEC_HEADERS devices/CECRecordingDevice.h adapter/RPi/RPiCECAdapterMessageQueue.h adapter/RPi/RPiCECAdapterCommunication.h adapter/RPi/RPiCECAdapterDetection.h + adapter/IMX/IMXCECAdapterCommunication.h + adapter/IMX/IMXCECAdapterDetection.h CECInputBuffer.h platform/util/baudrate.h platform/util/edid.h @@ -155,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) @@ -172,18 +182,56 @@ 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) -# 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/LibCEC.cpp b/src/libcec/LibCEC.cpp index 8eedaa7b..cf5de495 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 @@ -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; @@ -624,3 +624,12 @@ bool CLibCEC::AudioEnable(bool enable) m_client->AudioEnable(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 diff --git a/src/libcec/LibCEC.h b/src/libcec/LibCEC.h index 7155926c..1fe3ced5 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 @@ -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/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/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 a88d9bab..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; @@ -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..0843e381 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 @@ -66,12 +66,13 @@ 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; } 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); @@ -83,6 +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 */ @@ -97,7 +101,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..8af9b227 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 @@ -175,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. @@ -231,6 +238,10 @@ 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/AdapterFactory.cpp b/src/libcec/adapter/AdapterFactory.cpp index 91195ea0..ede5f375 100644 --- a/src/libcec/adapter/AdapterFactory.cpp +++ b/src/libcec/adapter/AdapterFactory.cpp @@ -58,11 +58,21 @@ #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" #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 */) @@ -131,6 +141,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()) { @@ -143,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_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 @@ -163,6 +197,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); @@ -173,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_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/Exynos/ExynosCECAdapterCommunication.cpp b/src/libcec/adapter/Exynos/ExynosCECAdapterCommunication.cpp index 285110ff..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; @@ -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..8f00c7c0 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 @@ -64,13 +64,14 @@ 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; } 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; } @@ -81,6 +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 */ @@ -96,7 +100,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/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 new file mode 100644 index 00000000..255feed2 --- /dev/null +++ b/src/libcec/adapter/IMX/IMXCECAdapterCommunication.cpp @@ -0,0 +1,289 @@ +/* + * 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 "p8-platform/sockets/cdevsocket.h" +#include "CECTypeUtils.h" +#include "LibCEC.h" + +#include "IMXCEC.h" + +using namespace std; +using namespace CEC; +using namespace P8PLATFORM; + +#define LIB_CEC m_callback->GetLib() + + +CIMXCECAdapterCommunication::CIMXCECAdapterCommunication(IAdapterCommunicationCallback *callback) : + IAdapterCommunication(callback) +{ + CLockObject lock(m_mutex); + + m_iNextMessage = 0; + 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)) +{ + int error, msg_len = 1; + unsigned char message[MAX_CEC_MESSAGE_LEN]; + + 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) + return ADAPTER_MESSAGE_STATE_SENT_ACKED; + + error = m_dev->GetErrorNumber(); + if (error == EIO) + return ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED; + + 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 1; +} + + +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) const +{ + 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) +{ + hdmi_cec_event event; + int ret; + + cec_logical_address initiator, destination; + + while (!IsStopped()) + { + ret = m_dev->Read((char *)&event, sizeof(event), 1000); + 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..fdf21d4b --- /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 "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*/ +#define IMX_ADAPTER_PID 0x1001 + + + +namespace P8PLATFORM +{ + class CCDevSocket; +}; + + +namespace CEC +{ + class CIMXCECAdapterCommunication : public IAdapterCommunication, public P8PLATFORM::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) const; + 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; } + 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; } + 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)) {} +#if CEC_LIB_VERSION_MAJOR >= 5 + bool GetStats(struct cec_adapter_stats* UNUSED(stats)) { return false; } +#endif + ///} + + /** @name P8PLATFORM::CThread implementation */ + ///{ + void *Process(void); + ///} + + 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 */ + + cec_logical_address m_logicalAddress; + + mutable P8PLATFORM::CMutex m_mutex; + P8PLATFORM::CCDevSocket *m_dev; /**< the device connection */ + bool m_bLogicalAddressRegistered; + bool m_bInitialised; + + P8PLATFORM::CMutex m_messageMutex; + uint32_t m_iNextMessage; + }; + +}; + +#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); + }; +} 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..f3519d40 --- /dev/null +++ b/src/libcec/adapter/Linux/LinuxCECAdapterCommunication.h @@ -0,0 +1,98 @@ +#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 {} +#if CEC_LIB_VERSION_MAJOR >= 5 + bool GetStats(struct cec_adapter_stats* UNUSED(stats)) override { return false; } +#endif + ///} + + /** @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/adapter/Pulse-Eight/USBCECAdapterCommands.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.cpp index a271f74a..67cc651e 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; @@ -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); @@ -129,6 +131,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 @@ -231,21 +249,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"); + m_persistedConfiguration.strDeviceName[0] = (char)0; 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 <= 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; } @@ -413,6 +428,42 @@ bool CUSBCECAdapterCommands::SetSettingPhysicalAddress(uint16_t iPhysicalAddress return bReturn; } +bool CUSBCECAdapterCommands::SetSettingAutoPowerOn(bool autoOn) +{ + bool bReturn(false); + + if (m_persistedConfiguration.iFirmwareVersion < 10) + // only supported by v10+ + 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); @@ -460,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; } @@ -498,13 +549,19 @@ 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)); bReturn |= SetSettingPhysicalAddress(configuration.iPhysicalAddress); - bReturn |= SetSettingCECVersion(configuration.cecVersion); 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); return bReturn; } @@ -522,13 +579,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 >= 10) + bReturn |= RequestSettingAutoPowerOn(); + else + bReturn |= RequestSettingCECVersion(); // don't read the following settings: // - auto enabled (always enabled) @@ -563,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/adapter/Pulse-Eight/USBCECAdapterCommands.h b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommands.h index 60a112a1..552c5cf0 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 @@ -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. @@ -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/USBCECAdapterCommunication.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterCommunication.cpp index fecc0446..b048cb8b 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" @@ -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()) @@ -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; @@ -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); @@ -635,6 +635,15 @@ 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) { return GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE && @@ -648,6 +657,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; @@ -710,6 +726,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 63a73a68..b8d2459e 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" @@ -77,12 +77,13 @@ 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); 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); @@ -93,10 +94,19 @@ 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); + void OnRxSuccess(void); + void OnRxError(void); + void OnTxAck(void); + void OnTxNack(void); + void OnTxError(void); + void *Process(void); private: @@ -131,7 +141,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 +184,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 */ @@ -184,6 +194,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/USBCECAdapterDetection.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterDetection.cpp index 5d9ee9bf..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 @@ -68,10 +67,16 @@ extern "C" { #include #endif +#if defined(__linux__) +#include +#include +#include +#endif + #include #include #include -#include +#include "p8-platform/util/StringUtils.h" #define CEC_VID 0x2548 #define CEC_PID 0x1001 @@ -79,7 +84,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 +135,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; @@ -138,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) @@ -270,7 +271,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) { @@ -356,6 +357,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) @@ -411,6 +414,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); @@ -506,6 +572,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); }; }; diff --git a/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.cpp b/src/libcec/adapter/Pulse-Eight/USBCECAdapterMessage.cpp index b7b891cb..64bb59c8 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; } @@ -240,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) @@ -332,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; } @@ -366,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; } @@ -414,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 @@ -465,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 2e8123dc..fefe056d 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, @@ -93,6 +95,8 @@ namespace CEC P8_ADAPTERTYPE_DAUGHTERBOARD, } p8_cec_adapter_type; + #define USBCEC_MAX_MSG_SIZE (18 * 4) + class CCECAdapterMessage { public: @@ -143,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. @@ -254,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 1ccac14b..064cc120 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; @@ -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()) @@ -213,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; @@ -237,7 +239,9 @@ 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 { @@ -261,13 +265,31 @@ 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; + 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()) - 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 +387,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 +398,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 +409,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(); @@ -392,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); - { - CLockObject lock(m_mutex); - bFullMessage = m_incomingAdapterMessage->PushReceivedByte(data[iPtr]); - } - - if (bFullMessage) + if (m_incomingAdapterMessage->PushReceivedByte(data[ptr])) { - /* 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(); } } 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.cpp b/src/libcec/adapter/RPi/RPiCECAdapterCommunication.cpp index 64fb084c..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); @@ -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)) @@ -431,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..4c2a4eba 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 @@ -71,13 +71,14 @@ 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; }; 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; }; @@ -87,6 +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); @@ -96,7 +100,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 +112,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/RPi/RPiCECAdapterMessageQueue.cpp b/src/libcec/adapter/RPi/RPiCECAdapterMessageQueue.cpp index 4927ec62..1f827204 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 @@ -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 - 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 734e3bb7..5d115b2c 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__ @@ -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); @@ -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)); } } @@ -294,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__); @@ -363,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); + } + } } } diff --git a/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h b/src/libcec/adapter/TDA995x/TDA995xCECAdapterCommunication.h index 47dde4be..f3cfb760 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 @@ -75,13 +75,14 @@ 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; } 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; } @@ -92,6 +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 */ @@ -107,14 +111,14 @@ 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; uint32_t m_iNextMessage; std::map m_messages; }; - + }; #endif diff --git a/src/libcec/cmake/CheckPlatformSupport.cmake b/src/libcec/cmake/CheckPlatformSupport.cmake index 6875606c..1d99bd87 100644 --- a/src/libcec/cmake/CheckPlatformSupport.cmake +++ b/src/libcec/cmake/CheckPlatformSupport.cmake @@ -9,7 +9,9 @@ # 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_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 @@ -21,6 +23,7 @@ set(RPI_INCLUDE_DIR "" CACHE STRING "Path to Raspberry Pi headers") set(PLATFORM_LIBREQUIRES "") include(CheckFunctionExists) +include(CheckSymbolExists) include(FindPkgConfig) # defaults @@ -29,6 +32,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) @@ -42,11 +46,24 @@ if(WIN32) # Windows add_definitions(-DTARGET_WINDOWS -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -D_WINSOCKAPI_) set(LIB_DESTINATION ".") - if (${WIN64}) - 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") @@ -137,6 +154,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") @@ -148,6 +175,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 @@ -162,14 +201,34 @@ 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) + endif() + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14") + # old style swig + 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") @@ -177,33 +236,61 @@ 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}) 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) + else() + if (${PYTHON_MAJOR_VERSION} GREATER 2) + 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) + DESTINATION python/cec) + if (${PYTHON_MAJOR_VERSION} EQUAL 2) + install(FILES ${CMAKE_SOURCE_DIR}/src/libcec/cmake/__init__.py + DESTINATION python/cec) + endif() 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 - 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_SOURCE_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/DisplayPlatformSupport.cmake b/src/libcec/cmake/DisplayPlatformSupport.cmake index 83a778af..8b2a558f 100644 --- a/src/libcec/cmake/DisplayPlatformSupport.cmake +++ b/src/libcec/cmake/DisplayPlatformSupport.cmake @@ -44,12 +44,24 @@ 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() 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/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/libcec/cmake/SetBuildInfo.cmake b/src/libcec/cmake/SetBuildInfo.cmake index 39bf4be0..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 @@ -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) @@ -45,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() 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 ## diff --git a/src/libcec/devices/CECBusDevice.cpp b/src/libcec/devices/CECBusDevice.cpp index 99f762c6..32406540 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" @@ -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); @@ -300,6 +301,18 @@ bool CCECBusDevice::IsHandledByLibCEC(void) return m_deviceStatus == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC; } +bool CCECBusDevice::IsActive(bool suppressPoll /* = true */) +{ + 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) { // some commands should never be marked as unsupported @@ -685,6 +698,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(); } } @@ -840,6 +856,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 || m_iLogicalAddress != CECDEVICE_TV) && // poll forced (bForcePoll || // don't know the status @@ -1141,6 +1159,7 @@ bool CCECBusDevice::TransmitActiveSource(bool bIsReply) if (bSendActiveSource) { MarkBusy(); + SetActiveSourceSent(true); bActiveSourceSent = m_handler->TransmitActiveSource(m_iLogicalAddress, iPhysicalAddress, bIsReply); MarkReady(); } @@ -1215,7 +1234,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 +1517,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_bActiveSourceSent = setto; +} + +bool CCECBusDevice::ActiveSourceSent(void) const +{ + return m_bActiveSourceSent; +} diff --git a/src/libcec/devices/CECBusDevice.h b/src/libcec/devices/CECBusDevice.h index b8255aab..19a43cc3 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 @@ -111,6 +111,7 @@ namespace CEC virtual const char* GetLogicalAddressName(void) const; virtual bool IsPresent(void); virtual bool IsHandledByLibCEC(void); + virtual bool IsActive(bool suppressPoll = true); virtual bool HandleCommand(const cec_command &command); virtual bool IsUnsupportedFeature(cec_opcode opcode); @@ -199,6 +200,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 +247,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..4d599ce8 100644 --- a/src/libcec/devices/CECDeviceMap.cpp +++ b/src/libcec/devices/CECDeviceMap.cpp @@ -175,15 +175,29 @@ 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(false)) { + devices.push_back(it->second); + } } } +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(suppressPoll))) { + return true; + } + } + return false; +} + void CCECDeviceMap::GetPowerOffDevices(const libcec_configuration &configuration, CECDEVICEVEC &devices) const { for (CECDEVICEMAP::const_iterator it = m_busDevices.begin(); it != m_busDevices.end(); it++) @@ -212,6 +226,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..7c3e8f71 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, 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); @@ -71,6 +72,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); 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..5f5fde5d 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__) @@ -73,9 +73,15 @@ /* 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@ +/* 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@ diff --git a/src/libcec/implementations/ANCommandHandler.cpp b/src/libcec/implementations/ANCommandHandler.cpp index 89769e6a..19464ccb 100644 --- a/src/libcec/implementations/ANCommandHandler.cpp +++ b/src/libcec/implementations/ANCommandHandler.cpp @@ -53,6 +53,11 @@ CANCommandHandler::CANCommandHandler(CCECBusDevice *busDevice, { m_vendorId = CEC_VENDOR_SAMSUNG; m_bOPTSendDeckStatusUpdateOnActiveSource = false; + if (busDevice->GetLogicalAddress() == CECDEVICE_TV) + { + // disable auto mode, as this may wake up the TV randomly (samsung 2017+ bug) + m_busDevice->GetProcessor()->SetAutoMode(false); + } } int CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command) @@ -91,7 +96,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/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..09087f13 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; @@ -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; 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..e905dc68 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" @@ -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 dbdd01ce..34dec5ab 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" @@ -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) @@ -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); + HandleVendorCommandPowerOnStatus(command); 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); 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 || 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..ab47852c 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 @@ -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) { 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/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; } 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; diff --git a/src/platform b/src/platform index 6535e48d..1c9d14fa 160000 --- a/src/platform +++ b/src/platform @@ -1 +1 @@ -Subproject commit 6535e48d68d69264c32d46ab9386ac18f77da5f7 +Subproject commit 1c9d14fa996af33760a2c700caebd2bd9ae527c9 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/.) diff --git a/src/pyCecClient/pyCecClient.py b/src/pyCecClient/pyCecClient.py index 6d478577..8b18d88b 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. @@ -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': diff --git a/support b/support index 63ec40a9..7c412111 160000 --- a/support +++ b/support @@ -1 +1 @@ -Subproject commit 63ec40a9e2dbe26d49dba0a73071a410dcc6b73b +Subproject commit 7c4121112af37d641ea02383d53b50a38087115a diff --git a/windows/build-all.cmd b/windows/build-all.cmd new file mode 100644 index 00000000..8e244fb5 --- /dev/null +++ b/windows/build-all.cmd @@ -0,0 +1,130 @@ +@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" +rem Restore nuget dependencies +msbuild -t:restore +"%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 d8a3f705..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 @@ -8,36 +9,58 @@ 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 +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% -CALL %MYDIR%..\src\platform\windows\build-lib.cmd %BUILDARCH% %BUILDTYPE% %VSVERSION% %INSTALLPATH% -del /s /f /q %BUILDTARGET% +rem Compile platform library +ECHO. * compiling platform library for %BUILDARCH% +CALL "%MYDIR%..\src\platform\windows\build-lib.cmd" %BUILDARCH% %BUILDTYPE% %VSVERSION% "%INSTALLPATH%" +RMDIR /s /q "%BUILDTARGET%" >nul 2>&1 + +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% -ECHO Build libCEC for %BUILDARCH% -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 + 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 4f5b634c..00000000 --- a/windows/build.cmd +++ /dev/null @@ -1,38 +0,0 @@ -@ECHO OFF - -rem Build libCEC for Windows - -SETLOCAL - -SET MYDIR=%~dp0 -SET BUILDTYPE=Release -SET VSVERSION=12 -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 - -FOR %%T IN (amd64 x86) DO ( - CALL %MYDIR%build-lib.cmd %%T %BUILDTYPE% %VSVERSION% %INSTALLPATH% nmake - IF NOT ERRORLEVEL 0 ( - GOTO builderror - ) -) - -rmdir /s /q %MYDIR%..\build\cmake -exit /b 0 - -:builderror -echo "Failed to build" - -:exit -exit /b 1 \ No newline at end of file diff --git a/windows/create-installer.cmd b/windows/create-installer.cmd index bc49309e..43a62656 100644 --- a/windows/create-installer.cmd +++ b/windows/create-installer.cmd @@ -1,131 +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 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 +SET MYDIR=%~dp0 -rmdir /s /q %MYDIR%..\build -call 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" +SET BUILDPATH=%MYDIR%..\build +SET EXITCODE=1 -rem Skip to libCEC/x86 when we're running on win32 -if "%PROCESSOR_ARCHITECTURE%"=="x86" if "%PROCESSOR_ARCHITEW6432%"=="" goto libcecx86 +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 Compile libCEC and cec-client x64 -echo. Cleaning libCEC (x64) -%COMPILER12% libcec.sln /Clean "Release|x64" -echo. Compiling libCEC (x64) -%COMPILER12% libcec.sln /Build "Release|x64" /Project LibCecSharp -%COMPILER12% libcec.sln /Build "Release|x64" -echo. Compiling .Net applications -cd "%MYDIR%..\src\dotnet\project" -%COMPILER12% 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 +rem Skip to libCEC/x86 if we're running on win32 +IF "%PROCESSOR_ARCHITECTURE%"=="x86" IF "%PROCESSOR_ARCHITEW6432%"=="" GOTO libcecx86 + +: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 +) :libcecx86 -rem Compile libCEC and cec-client Win32 -cd "%MYDIR%..\project" -echo. Cleaning libCEC (x86) -%COMPILER12% libcec.sln /Clean "Release|x86" -echo. Compiling libCEC (x86) -%COMPILER12% libcec.sln /Build "Release|x86" /Project LibCecSharp -%COMPILER12% libcec.sln /Build "Release|x86" -echo. Compiling .Net applications -cd "%MYDIR%..\src\dotnet\project" -%COMPILER12% 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" +CALL "%MYDIR%build-all.cmd" x86 %BUILDTYPE% %VSVERSION% +IF %errorlevel% neq 0 ( + ECHO. *** failed to build libCEC for x86 *** + SET EXITCODE=1 + GOTO EXIT +) -rem Clean things up before creating the installer -del /q /f %MYDIR%..\build\x86\LibCecSharp.pdb -del /q /f %MYDIR%..\build\amd64\LibCecSharp.pdb +:CREATEEGPLUGIN +ECHO. * creating EventGhost plugin +SET EGSOURCES=%MYDIR%..\src\eventghost\egplugin_sources\ +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" >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" ( + ECHO. *** failed to create EventGhost plugin *** + SET EXITCODE=1 + 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 -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 +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 :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 :ERRORCREATINGINSTALLER - -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% +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 ) -echo. The installer can be found here: %INSTALLER% -set EXITCODE=0 -GOTO EXIT - -:NOSDK11 -echo. Visual Studio 2012 was not found on your system. -GOTO EXIT - -:NOSIS -echo. NSIS could not be found on your system. -GOTO EXIT - -:NODDK -echo. Windows DDK could not be found on your system -GOTO EXIT - -:ERRORCREATINGINSTALLER -echo. The installer could not be created. The most likely cause is that something went wrong while compiling. -GOTO RETURNEXIT +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 4b670493..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=12 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%.