From a393f6211c90c7ef26c4d296a9de1d8ba9d9e074 Mon Sep 17 00:00:00 2001 From: Rastafabisch <38974866+Rastafabisch@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:39:18 +0100 Subject: [PATCH 01/98] Update Info.plist.in (#1575) Hide Hyperion from the Dock. --- cmake/osxbundle/Info.plist.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/osxbundle/Info.plist.in b/cmake/osxbundle/Info.plist.in index 40e6df5e..9774dd79 100644 --- a/cmake/osxbundle/Info.plist.in +++ b/cmake/osxbundle/Info.plist.in @@ -24,6 +24,8 @@ 6.0 CFBundlePackageType APPL + LSUIElement + 1 NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} Source Code From e59e456b0042be5c70aa7c0978fbf4fdb2ef00d4 Mon Sep 17 00:00:00 2001 From: Harald Date: Mon, 27 Feb 2023 17:04:34 +0100 Subject: [PATCH 02/98] fix python version on Debian bookworm (fix #1579) (#1580) the package wanted the dependency libpython3.9 but the executable expects libpython3.10 --- .github/workflows/apt/amd64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/apt/amd64.json b/.github/workflows/apt/amd64.json index 9e0cce1b..90e04ff7 100644 --- a/.github/workflows/apt/amd64.json +++ b/.github/workflows/apt/amd64.json @@ -59,7 +59,7 @@ "distribution": "Bookworm", "architecture": "amd64", "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", + "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Debian 12.x (Bookworm) (amd64)" } From c73f8623b81842584293fbc868ce98ed350e2944 Mon Sep 17 00:00:00 2001 From: Hyperion-Bot <20935312+Hyperion-Bot@users.noreply.github.com> Date: Tue, 7 Mar 2023 00:26:14 +0000 Subject: [PATCH 03/98] Update submodule rpi_ws281x --- dependencies/external/rpi_ws281x | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/external/rpi_ws281x b/dependencies/external/rpi_ws281x index 1ba8e385..15330cb3 160000 --- a/dependencies/external/rpi_ws281x +++ b/dependencies/external/rpi_ws281x @@ -1 +1 @@ -Subproject commit 1ba8e385708fb7802b09c0177a7ea4293948e25c +Subproject commit 15330cb384aed2411ec7e42712ad4ed7af940877 From 85eb62f31490849bba7612ce4302af271d470c80 Mon Sep 17 00:00:00 2001 From: Portisch Date: Thu, 9 Mar 2023 08:38:05 +0100 Subject: [PATCH 04/98] Fix missing Include (#1585) 'std::numeric_limits' do need '#include ' --- CHANGELOG.md | 1 + libsrc/utils/jsonschema/QJsonSchemaChecker.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0daa08c..ce4dead4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed ### Fixed +- Fixed missing Include limits in QJsonSchemaChecker ## Removed diff --git a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp index 5cec2ec3..685ab4c7 100644 --- a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp +++ b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp @@ -1,4 +1,5 @@ // stdlib includes +#include #include #include #include From 2f09f9a0b85e56cc013135c89dfa2ca7b447fec5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Mar 2023 21:38:08 +0100 Subject: [PATCH 05/98] Bump actions/upload-artifact from 2.2.0 to 3.1.2 (#1583) GitHub Actions Update --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ac3b57ba..1565d699 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -68,7 +68,7 @@ jobs: with: sarif_file: sarif-results/${{ matrix.language }}.sarif - name: Upload loc as a Build Artifact - uses: actions/upload-artifact@v2.2.0 + uses: actions/upload-artifact@v3 with: name: sarif-results path: sarif-results From e0060eb406dd6fdd78bf77456f4aa80324f035e2 Mon Sep 17 00:00:00 2001 From: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com> Date: Sun, 26 Mar 2023 15:22:06 +0200 Subject: [PATCH 06/98] cleanup --- .codedocs | 76 --------------------------------- .lgtm.yml | 27 ------------ bin/scripts/docker-compile.sh | 2 +- doc/development/CompileHowto.md | 20 +-------- 4 files changed, 3 insertions(+), 122 deletions(-) delete mode 100644 .codedocs delete mode 100644 .lgtm.yml diff --git a/.codedocs b/.codedocs deleted file mode 100644 index ead816de..00000000 --- a/.codedocs +++ /dev/null @@ -1,76 +0,0 @@ -# Hyperion.NG .codedocs Configuration File - -#--------------------------------------------------------------------------- -# CodeDocs Configuration -#--------------------------------------------------------------------------- - -# Include the Doxygen configuration from another file. -# The file must be a relative path with respect to the root of the repository. - -DOXYFILE = - -# Specify external repository to link documentation with. -# This is similar to Doxygen's TAGFILES option, but will automatically link to -# tags of other repositories already using CodeDocs. List each repository to -# link with by giving its location in the form of owner/repository. -# For example: -# TAGLINKS = doxygen/doxygen CodeDocs/osg -# Note: these repositories must already be built on CodeDocs. - -TAGLINKS = - -#--------------------------------------------------------------------------- -# Doxygen Configuration -#--------------------------------------------------------------------------- - -# Doxygen configuration may also be placed in this file. -# Currently, the following Doxygen configuration options are available. Refer -# to http://doxygen.org/manual/config.html for detailed explanation of the -# options. To request support for more options, contact support@codedocs.xyz. -# -# ABBREVIATE_BRIEF = -# ALIASES = -# ALPHABETICAL_INDEX = -# ALWAYS_DETAILED_SEC = -# CASE_SENSE_NAMES = -# CLASS_DIAGRAMS = -# DISABLE_INDEX = -# DISTRIBUTE_GROUP_DOC = -# EXAMPLE_PATH = - EXCLUDE = .ci/ \ - assets/ \ - bin/ - config/ \ - effects/ \ - test/ \ -# EXCLUDE_PATTERNS = -# EXCLUDE_SYMBOLS = -# EXTENSION_MAPPING = -# EXTRACT_LOCAL_CLASSES = -# FILE_PATTERNS = -# GENERATE_TAGFILE = -# GENERATE_TREEVIEW = -# HIDE_COMPOUND_REFERENCE = -# HIDE_SCOPE_NAMES = -# HIDE_UNDOC_CLASSES = -# HIDE_UNDOC_MEMBERS = -# HTML_TIMESTAMP = -# INLINE_GROUPED_CLASSES = -# INPUT_ENCODING = -# INTERNAL_DOCS = -# OPTIMIZE_OUTPUT_FOR_C = - PROJECT_BRIEF = "The successor to Hyperion aka Hyperion Next Generation" - PROJECT_NAME = "Hyperion.NG" -# PROJECT_NUMBER = -# SHORT_NAMES = -# SHOW_FILES = -# SHOW_INCLUDE_FILES = -# SHOW_NAMESPACES = -# SORT_BRIEF_DOCS = -# SORT_BY_SCOPE_NAME = -# SORT_MEMBER_DOCS = -# STRICT_PROTO_MATCHING = -# TYPEDEF_HIDES_STRUCT = - USE_MDFILE_AS_MAINPAGE = README.md -# VERBATIM_HEADERS = -# diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index 6a799607..00000000 --- a/.lgtm.yml +++ /dev/null @@ -1,27 +0,0 @@ -extraction: - cpp: - prepare: - packages: - - "git" - - "cmake" - - "build-essential" - - "qtbase5-dev" - - "libqt5serialport5-dev" - - "libqt5sql5-sqlite" - - "libqt5svg5-dev" - - "libqt5x11extras5-dev" - - "libusb-1.0-0-dev" - - "python3-dev" - - "libcec-dev" - - "libxcb-image0-dev" - - "libxcb-util0-dev" - - "libxcb-shm0-dev" - - "libxcb-render0-dev" - - "libxcb-randr0-dev" - - "libxrandr-dev" - - "libxrender-dev" - - "libavahi-core-dev" - - "libavahi-compat-libdnssd-dev" - - "libturbojpeg0-dev" - - "libjpeg-dev" - - "libssl-dev" diff --git a/bin/scripts/docker-compile.sh b/bin/scripts/docker-compile.sh index 5381529a..a6abd11d 100755 --- a/bin/scripts/docker-compile.sh +++ b/bin/scripts/docker-compile.sh @@ -72,7 +72,7 @@ echo "######################################################## # These are possible arguments to modify the script behaviour with their default values # # docker-compile.sh -h, --help # Show this help message -# docker-compile.sh -i, --image # The docker image, e.g., x86_64, armv6l, armv7l, aarch64, rpi-raspbian +# docker-compile.sh -i, --image # The docker image, e.g., x86_64, armv6l, armv7l, aarch64 # docker-compile.sh -t, --tag # The docker tag, e.g., stretch, buster, bullseye, bookworm # docker-compile.sh -b, --type # Release or Debug build # docker-compile.sh -p, --packages # If true, build packages with CPack diff --git a/doc/development/CompileHowto.md b/doc/development/CompileHowto.md index f3b5502c..8a2cc7df 100644 --- a/doc/development/CompileHowto.md +++ b/doc/development/CompileHowto.md @@ -5,24 +5,6 @@ If you are using [Docker](https://www.docker.com/), you can compile Hyperion ins The compiled binaries and packages will be available at the deploy folder next to the script.
Note: call the script with `./docker-compile.sh -h` for more options. -## Native compilation on Raspberry Pi for: - -**Raspbian Stretch** -```console -wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i rpi-raspbian -t stretch -``` -**Raspbian Buster/Raspberry Pi OS** -```console -wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i rpi-raspbian -t buster -``` -**Raspberry Pi OS Bullseye** -```console -wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i rpi-raspbian -t bullseye -``` -**Raspberry Pi OS Bookworm** -```console -wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i rpi-raspbian -t bookworm -``` ## Cross compilation on x86_64 for: **x86_64 (Debian Stretch):** @@ -69,6 +51,7 @@ wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/ ```console wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i armv7l -t bullseye ``` + ## Cross compilation on x86_64 for developers Using additional options you can cross compile locally -l: use a local hyperion source code directory rather than cloning from GitHub @@ -79,6 +62,7 @@ Using additional options you can cross compile locally cd $HYPERION_HOME ./bin/scripts/docker-compile.sh -l -c -i armv7l -t bullseye ``` + # The usual way ## Debian/Ubuntu/Win10LinuxSubsystem From 08e7c7d8c2a40fbb1fa284461925cc4809a313ab Mon Sep 17 00:00:00 2001 From: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com> Date: Mon, 27 Mar 2023 20:54:34 +0200 Subject: [PATCH 07/98] [HotFix] Supplement to the PR #1570 - Fix Issue #1596 --- .github/workflows/apt/arm64.json | 28 ++++++++++++++-------------- .github/workflows/apt/armhf.json | 32 ++++++++++++++++---------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/apt/arm64.json b/.github/workflows/apt/arm64.json index 8cc8b2e5..5f64a3ab 100644 --- a/.github/workflows/apt/arm64.json +++ b/.github/workflows/apt/arm64.json @@ -2,56 +2,56 @@ { "distribution": "Bionic", "architecture": "arm64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev", - "package-depends": "libpython3.6, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libturbojpeg, libcec4", + "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev", + "package-depends": "libpython3.6, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg, libcec4", "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Ubuntu 18.04 (Bionic Beaver) (arm64)" }, { "distribution": "Focal", "architecture": "arm64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.8, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg, libcec4", + "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", + "package-depends": "libpython3.8, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg, libcec4", "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Ubuntu 20.04 (Focal Fossa) (arm64)" }, { "distribution": "Jammy", "architecture": "arm64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libturbojpeg, libcec6", + "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", + "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Ubuntu 22.04 (Jammy Jellyfish) (arm64)" }, { "distribution": "Kinetic", "architecture": "arm64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libturbojpeg, libcec6", + "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", + "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Ubuntu 22.10 (Kinetic Kudu) (arm64)" }, { "distribution": "Buster", "architecture": "arm64", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.7, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec4", + "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", + "package-depends": "libpython3.7, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec4", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Debian 10.x (Buster) (arm64)" }, { "distribution": "Bullseye", "architecture": "arm64", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec6", + "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", + "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Debian 11.x (Bullseye) (arm64)" }, { "distribution": "Bookworm", "architecture": "arm64", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec6", + "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", + "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Debian 12.x (Bookworm) (arm64)", "exclude" : true diff --git a/.github/workflows/apt/armhf.json b/.github/workflows/apt/armhf.json index f7f5aa3c..33847459 100644 --- a/.github/workflows/apt/armhf.json +++ b/.github/workflows/apt/armhf.json @@ -2,64 +2,64 @@ { "distribution": "Bionic", "architecture": "armhf", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev", - "package-depends": "libpython3.6, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libturbojpeg, libcec4", + "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev", + "package-depends": "libpython3.6, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg, libcec4", "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Ubuntu 18.04 (Bionic Beaver) (armhf)" }, { "distribution": "Focal", "architecture": "armhf", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.8, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg, libcec4", + "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", + "package-depends": "libpython3.8, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg, libcec4", "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Ubuntu 20.04 (Focal Fossa) (armhf)" }, { "distribution": "Jammy", "architecture": "armhf", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libturbojpeg, libcec6", + "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", + "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Ubuntu 22.04 (Jammy Jellyfish) (armhf)" }, { "distribution": "Kinetic", "architecture": "armhf", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libturbojpeg, libcec6", + "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", + "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Ubuntu 22.10 (Kinetic Kudu) (armhf)" }, { "distribution": "Stretch", "architecture": "armhf", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl1.0-dev, libraspberrypi-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.5, libusb-1.0-0, libqt5widgets5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libturbojpeg0, libcec4", + "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl1.0-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", + "package-depends": "libpython3.5, libusb-1.0-0, libqt5widgets5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg0, libcec4", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description":"Debian 9.x (Stretch) (armhf)" }, { "distribution": "Buster", "architecture": "armhf", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl1.0-dev, libraspberrypi-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.7, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec4", + "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl1.0-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", + "package-depends": "libpython3.7, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec4", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Debian 10.x (Buster) (armhf)" }, { "distribution": "Bullseye", "architecture": "armhf", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec6", + "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", + "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Debian 11.x (Bullseye) (armhf)" }, { "distribution": "Bookworm", "architecture": "armhf", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec6", + "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", + "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Debian 12.x (Bookworm) (armhf)", "exclude" : true From e3496eb4dc1d6d9efeb8a013ca741ab53c0ec564 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 17:25:49 +0200 Subject: [PATCH 08/98] Bump SamKirkland/FTP-Deploy-Action from 4.3.3 to 4.3.4 (#1598) Bumps [SamKirkland/FTP-Deploy-Action](https://github.com/SamKirkland/FTP-Deploy-Action) from 4.3.3 to 4.3.4. - [Release notes](https://github.com/SamKirkland/FTP-Deploy-Action/releases) - [Commits](https://github.com/SamKirkland/FTP-Deploy-Action/compare/4.3.3...v4.3.4) --- updated-dependencies: - dependency-name: SamKirkland/FTP-Deploy-Action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/apt.yml | 2 +- .github/workflows/nightly.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/apt.yml b/.github/workflows/apt.yml index fc5eca13..41cc80f9 100644 --- a/.github/workflows/apt.yml +++ b/.github/workflows/apt.yml @@ -121,7 +121,7 @@ jobs: done - name: Upload packages to APT server (DRAFT) - uses: SamKirkland/FTP-Deploy-Action@4.3.3 + uses: SamKirkland/FTP-Deploy-Action@v4.3.4 with: server: apt.hyperion-project.org username: ${{ secrets.APT_USER }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 75365c4c..00772c41 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -171,7 +171,7 @@ jobs: done - name: Upload packages to nightly server - uses: SamKirkland/FTP-Deploy-Action@4.3.3 + uses: SamKirkland/FTP-Deploy-Action@v4.3.4 with: server: nightly.apt.hyperion-project.org username: ${{ secrets.NIGHTLY_USER }} From 79b31e16e019a2ec4bdf607ed8bb38ac7af39504 Mon Sep 17 00:00:00 2001 From: Hyperion-Bot <20935312+Hyperion-Bot@users.noreply.github.com> Date: Wed, 26 Apr 2023 00:22:19 +0000 Subject: [PATCH 09/98] Update submodule rpi_ws281x --- dependencies/external/rpi_ws281x | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/external/rpi_ws281x b/dependencies/external/rpi_ws281x index 15330cb3..1f47b59e 160000 --- a/dependencies/external/rpi_ws281x +++ b/dependencies/external/rpi_ws281x @@ -1 +1 @@ -Subproject commit 15330cb384aed2411ec7e42712ad4ed7af940877 +Subproject commit 1f47b59ed603223d1376d36c788c89af67ae2fdc From 5535b884bfea368e92fc5bd24a2826e0bd9d62b3 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Sun, 7 May 2023 14:04:45 +0200 Subject: [PATCH 10/98] Fixes (#1605) * Correct misleading Error messages * Fix that Audio Capture is not shown when there is no screen nor video grabber * Fix - Audio Capture enabled after reboot automatically (#1581) --- assets/webconfig/js/content_index.js | 4 +++- libsrc/hyperion/CaptureCont.cpp | 6 +++--- libsrc/hyperion/GrabberWrapper.cpp | 2 +- src/hyperiond/SuspendHandler.cpp | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/assets/webconfig/js/content_index.js b/assets/webconfig/js/content_index.js index 5a5203ca..e8ec28a8 100644 --- a/assets/webconfig/js/content_index.js +++ b/assets/webconfig/js/content_index.js @@ -218,7 +218,9 @@ $(document).ready(function () { loadContent(undefined,true); //Hide capture menu entries, if no grabbers are available - if ((window.serverInfo.grabbers.screen.available.length === 0) && (window.serverInfo.grabbers.video.available.length === 0)) { + if ((window.serverInfo.grabbers.screen.available.length === 0) && + (window.serverInfo.grabbers.video.available.length === 0) && + (window.serverInfo.grabbers.audio.available.length === 0)) { $("#MenuItemGrabber").attr('style', 'display:none') if ((jQuery.inArray("boblight", window.serverInfo.services) === -1)) { $("#MenuItemInstCapture").attr('style', 'display:none') diff --git a/libsrc/hyperion/CaptureCont.cpp b/libsrc/hyperion/CaptureCont.cpp index 4f3a7aae..5ae2a9e5 100644 --- a/libsrc/hyperion/CaptureCont.cpp +++ b/libsrc/hyperion/CaptureCont.cpp @@ -97,7 +97,7 @@ void CaptureCont::setSystemCaptureEnable(bool enable) } else { - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, this, 0); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, this, nullptr); _hyperion->clear(_systemCaptPrio); _systemInactiveTimer->stop(); _systemCaptName = ""; @@ -120,7 +120,7 @@ void CaptureCont::setV4LCaptureEnable(bool enable) } else { - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setV4lImage, this, 0); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::setV4lImage, this, nullptr); _hyperion->clear(_v4lCaptPrio); _v4lInactiveTimer->stop(); _v4lCaptName = ""; @@ -143,7 +143,7 @@ void CaptureCont::setAudioCaptureEnable(bool enable) } else { - disconnect(GlobalSignals::getInstance(), &GlobalSignals::setAudioImage, this, 0); + disconnect(GlobalSignals::getInstance(), &GlobalSignals::setAudioImage, this, nullptr); _hyperion->clear(_audioCaptPrio); _audioInactiveTimer->stop(); _audioCaptName = ""; diff --git a/libsrc/hyperion/GrabberWrapper.cpp b/libsrc/hyperion/GrabberWrapper.cpp index 4d88a6f2..e0e4a27c 100644 --- a/libsrc/hyperion/GrabberWrapper.cpp +++ b/libsrc/hyperion/GrabberWrapper.cpp @@ -290,7 +290,7 @@ void GrabberWrapper::handleSourceRequest(hyperion::Components component, int hyp else GRABBER_AUDIO_CLIENTS.remove(hyperionInd); - if (GRABBER_AUDIO_CLIENTS.empty()) + if (GRABBER_AUDIO_CLIENTS.empty() || !getAudioGrabberState()) stop(); else start(); diff --git a/src/hyperiond/SuspendHandler.cpp b/src/hyperiond/SuspendHandler.cpp index a2014ef6..b680b3ea 100644 --- a/src/hyperiond/SuspendHandler.cpp +++ b/src/hyperiond/SuspendHandler.cpp @@ -268,7 +268,7 @@ SuspendHandlerLinux::SuspendHandlerLinux() QDBusConnection systemBus = QDBusConnection::systemBus(); if (!systemBus.isConnected()) { - Error(Logger::getInstance("DAEMON"), "Suspend/resume handler - System bus is not connected"); + Info(Logger::getInstance("DAEMON"), "The suspend/resume feature is not supported by your system configuration"); } else { @@ -288,7 +288,7 @@ SuspendHandlerLinux::SuspendHandlerLinux() QDBusConnection sessionBus = QDBusConnection::sessionBus(); if (!sessionBus.isConnected()) { - Error(Logger::getInstance("DAEMON"), "Lock/unlock handler- Session bus is not connected"); + Info(Logger::getInstance("DAEMON"), "The lock/unlock feature is not supported by your system configuration"); } else { From 2a17de37f16e0f23582645a5145352bb6aa76a59 Mon Sep 17 00:00:00 2001 From: lsellens Date: Thu, 11 May 2023 14:11:33 -0500 Subject: [PATCH 11/98] fix for matrix effect (#1602) --- effects/matrix.gif | Bin 0 -> 239117 bytes effects/matrix.json | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 effects/matrix.gif diff --git a/effects/matrix.gif b/effects/matrix.gif new file mode 100644 index 0000000000000000000000000000000000000000..f84fae90580130851a2d88fde387c4aa4cce7459 GIT binary patch literal 239117 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

k=Uw=n$zjOXW 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

$F;QP_%U;oW5Cobq6^6I>#jPb|OY1WF~sQBz(Hl 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 Ccdd;8 literal 0 HcmV?d00001 diff --git a/effects/matrix.json b/effects/matrix.json index c0ae4ea5..2a2f295c 100644 --- a/effects/matrix.json +++ b/effects/matrix.json @@ -9,8 +9,8 @@ "cropTop": 0, "fps": 30, "grayscale": false, - "imageSource": "url", + "imageSource": "file", "reverse": false, - "url": "https://i.gifer.com/embedded/download/1j6F.gif" + "file": "matrix.gif" } } From 54d03b80659a40e34f4ae5bf6011807fcf30bd68 Mon Sep 17 00:00:00 2001 From: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com> Date: Sun, 14 May 2023 16:29:57 +0200 Subject: [PATCH 12/98] added workflow_dispatch to allow the bot to create the APT repository itself --- .github/workflows/apt.yml | 27 +++++++++++++++++++++++++-- .github/workflows/push-master.yml | 8 +++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/.github/workflows/apt.yml b/.github/workflows/apt.yml index 41cc80f9..06212416 100644 --- a/.github/workflows/apt.yml +++ b/.github/workflows/apt.yml @@ -1,6 +1,26 @@ name: Hyperion APT Build on: workflow_call: + inputs: + head_sha: + type: string + description: The branch, tag or SHA to checkout + required: true + secrets: + APT_GPG: + required: true + APT_USER: + required: true + APT_PASSWORD: + required: true + APT_DRAFT: + required: true + workflow_dispatch: + inputs: + head_sha: + type: string + description: The branch, tag or SHA to checkout + required: true secrets: APT_GPG: required: true @@ -36,6 +56,7 @@ jobs: steps: - uses: actions/checkout@v3 with: + ref: ${{ github.event.inputs.head_sha || github.event.client_payload.head_sha }} submodules: true - name: Generate environment variables @@ -73,7 +94,7 @@ jobs: cp ../hyperion_*.deb /deploy" - name: Upload package artifact - if: startsWith(github.event.ref, 'refs/tags') + if: ${{ startsWith(github.event.ref, 'refs/tags') || github.event_name == 'workflow_dispatch' }} uses: actions/upload-artifact@v3 with: path: deploy @@ -81,12 +102,14 @@ jobs: publish: name: Publish APT packages - if: startsWith(github.event.ref, 'refs/tags') + if: ${{ startsWith(github.event.ref, 'refs/tags') || github.event_name == 'workflow_dispatch' }} needs: [setup, build] runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 + with: + ref: ${{ github.event.inputs.head_sha || github.event.client_payload.head_sha }} - name: Import GPG key uses: crazy-max/ghaction-import-gpg@v5.2.0 diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index c820a447..dc290a5b 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -195,8 +195,6 @@ jobs: if: startsWith(github.event.ref, 'refs/tags') needs: [Linux, macOS, windows] uses: ./.github/workflows/apt.yml - secrets: - APT_GPG: ${{ secrets.APT_GPG }} - APT_USER: ${{ secrets.APT_USER }} - APT_PASSWORD: ${{ secrets.APT_PASSWORD }} - APT_DRAFT: ${{ secrets.APT_DRAFT }} + secrets: inherit + with: + head_sha: master From 00ce3ff089a06718beddaf14dfca171149e55725 Mon Sep 17 00:00:00 2001 From: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com> Date: Mon, 29 May 2023 21:43:28 +0200 Subject: [PATCH 13/98] Update Discord Invite --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf3f7d8b..028922b1 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![CodeQL Analysis](https://github.com/hyperion-project/hyperion.ng/actions/workflows/codeql.yml/badge.svg)](https://github.com/hyperion-project/hyperion.ng/actions/workflows/codeql.yml) [![Forum](https://img.shields.io/website/https/hyperion-project.org.svg?label=Forum&down_color=red&down_message=offline&up_color=4bc51d&up_message=online&logo=homeadvisor&logoColor=white)](https://www.hyperion-project.org) [![Documentation](https://img.shields.io/website/https/docs.hyperion-project.org.svg?label=Documentation&down_color=red&down_message=offline&up_color=4bc51d&up_message=online&logo=read-the-docs)](https://docs.hyperion-project.org) -[![Discord](https://img.shields.io/discord/785578322167463937?label=Discord&logo=discord&logoColor=white&color=4bc51d)](https://discord.gg/khkR8Vx3ff) +[![Discord](https://img.shields.io/discord/785578322167463937?label=Discord&logo=discord&logoColor=white&color=4bc51d)](https://discord.gg/XtVTb3HEKS) ![made-with-love](https://img.shields.io/badge/Made%20With-♥-ff0000.svg) ## About Hyperion From a7ce0f8a4c61fa7bf132e2f96807ffba49cf7e37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Jun 2023 20:42:35 +0200 Subject: [PATCH 14/98] Bump crazy-max/ghaction-import-gpg from 5.2.0 to 5.3.0 (#1608) Bumps [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) from 5.2.0 to 5.3.0. - [Release notes](https://github.com/crazy-max/ghaction-import-gpg/releases) - [Commits](https://github.com/crazy-max/ghaction-import-gpg/compare/v5.2.0...v5.3.0) --- updated-dependencies: - dependency-name: crazy-max/ghaction-import-gpg dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/apt.yml | 2 +- .github/workflows/nightly.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/apt.yml b/.github/workflows/apt.yml index 06212416..66b90cb0 100644 --- a/.github/workflows/apt.yml +++ b/.github/workflows/apt.yml @@ -112,7 +112,7 @@ jobs: ref: ${{ github.event.inputs.head_sha || github.event.client_payload.head_sha }} - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v5.2.0 + uses: crazy-max/ghaction-import-gpg@v5.3.0 with: gpg_private_key: ${{ secrets.APT_GPG }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 00772c41..8a4da1e2 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -138,7 +138,7 @@ jobs: uses: actions/checkout@v3 - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v5.2.0 + uses: crazy-max/ghaction-import-gpg@v5.3.0 with: gpg_private_key: ${{ secrets.APT_GPG }} From af1a31b842a8aa7385e0e8ecde16be14c7c1260d Mon Sep 17 00:00:00 2001 From: Christoph Pohl Date: Sun, 25 Jun 2023 10:34:47 +0200 Subject: [PATCH 15/98] Update dependencies for Debian Bookworm (#1613) --- .github/workflows/apt/amd64.json | 2 +- .github/workflows/apt/arm64.json | 2 +- .github/workflows/apt/armhf.json | 2 +- CHANGELOG.md | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/apt/amd64.json b/.github/workflows/apt/amd64.json index 90e04ff7..7c794117 100644 --- a/.github/workflows/apt/amd64.json +++ b/.github/workflows/apt/amd64.json @@ -59,7 +59,7 @@ "distribution": "Bookworm", "architecture": "amd64", "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", + "package-depends": "libpython3.11, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg0, libcec6", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Debian 12.x (Bookworm) (amd64)" } diff --git a/.github/workflows/apt/arm64.json b/.github/workflows/apt/arm64.json index 5f64a3ab..5f0f233e 100644 --- a/.github/workflows/apt/arm64.json +++ b/.github/workflows/apt/arm64.json @@ -51,7 +51,7 @@ "distribution": "Bookworm", "architecture": "arm64", "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", + "package-depends": "libpython3.11, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg0, libcec6", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Debian 12.x (Bookworm) (arm64)", "exclude" : true diff --git a/.github/workflows/apt/armhf.json b/.github/workflows/apt/armhf.json index 33847459..66842969 100644 --- a/.github/workflows/apt/armhf.json +++ b/.github/workflows/apt/armhf.json @@ -59,7 +59,7 @@ "distribution": "Bookworm", "architecture": "armhf", "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", + "package-depends": "libpython3.11, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg0, libcec6", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Debian 12.x (Bookworm) (armhf)", "exclude" : true diff --git a/CHANGELOG.md b/CHANGELOG.md index ce4dead4..996ca847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed missing Include limits in QJsonSchemaChecker +- Fixed dependencies for deb packages in Debian Bookworm ## Removed From 64642a44578fe48dce8144c23180d29c160c7906 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Mon, 10 Jul 2023 20:39:35 +0200 Subject: [PATCH 16/98] Fix WLED UI handling non supported segment streaming (#1610) --- assets/webconfig/js/content_leds.js | 7 +++---- libsrc/leddevice/dev_net/LedDeviceWled.cpp | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js index 5cff7fd3..2a95af81 100755 --- a/assets/webconfig/js/content_leds.js +++ b/assets/webconfig/js/content_leds.js @@ -2331,6 +2331,7 @@ function updateElementsWled(ledType, key) { var enumSegSelectVals = []; var enumSegSelectTitleVals = []; var enumSegSelectDefaultVal = ""; + var defaultSegmentId = "-1"; if (devicesProperties[ledType] && devicesProperties[ledType][key]) { var ledDeviceProperties = devicesProperties[ledType][key]; @@ -2338,9 +2339,8 @@ function updateElementsWled(ledType, key) { if (!jQuery.isEmptyObject(ledDeviceProperties)) { if (ledDeviceProperties.info) { - if (ledDeviceProperties.info.liveseg && ledDeviceProperties.info.liveseg < 0) { + if (!ledDeviceProperties.info.hasOwnProperty("liveseg") || ledDeviceProperties.info.liveseg < 0) { // "Use main segment only" is disabled - var defaultSegmentId = "-1"; enumSegSelectVals.push(defaultSegmentId); enumSegSelectTitleVals.push($.i18n('edt_dev_spec_segments_disabled_title')); enumSegSelectDefaultVal = defaultSegmentId; @@ -2392,13 +2392,12 @@ function updateElementsWled(ledType, key) { hardwareLedCount = 1; } - if (segmentConfig) { + if (segmentConfig && segmentConfig.streamSegmentId > defaultSegmentId) { var configuredstreamSegmentId = window.serverConfig.device.segments.streamSegmentId.toString(); enumSegSelectVals = [configuredstreamSegmentId]; enumSegSelectTitleVals = ["Segment " + configuredstreamSegmentId]; enumSegSelectDefaultVal = configuredstreamSegmentId; } else { - defaultSegmentId = "-1"; enumSegSelectVals.push(defaultSegmentId); enumSegSelectTitleVals.push($.i18n('edt_dev_spec_segments_disabled_title')); enumSegSelectDefaultVal = defaultSegmentId; diff --git a/libsrc/leddevice/dev_net/LedDeviceWled.cpp b/libsrc/leddevice/dev_net/LedDeviceWled.cpp index 3aa4662f..3ada5091 100644 --- a/libsrc/leddevice/dev_net/LedDeviceWled.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceWled.cpp @@ -301,7 +301,7 @@ bool LedDeviceWled::isReadyForSegmentStreaming(semver::version& version) const } else { - Error(_log, "Version provided to test for streaming readiness is not valid "); + Error(_log, "Version provided to test for segment streaming readiness is not valid "); } return isReady; } @@ -325,7 +325,7 @@ bool LedDeviceWled::isReadyForDDPStreaming(semver::version& version) const } else { - Error(_log, "Version provided to test for streaming readiness is not valid "); + Error(_log, "Version provided to test for DDP streaming readiness is not valid "); } return isReady; } From 42c98da47046de9f0ff44f7990c6a4103d12385e Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Fri, 21 Jul 2023 16:45:37 +0200 Subject: [PATCH 17/98] Update platforms (#1617) * Remove stretch, bionic, add lunar * Fix CEC CMakeList for Ubuntu 23.04 * Fix git version identification when run in docker and local code * Update SYSTEM_LIBS_SKIP list * Updates after Ubuntu Server 20.04, latest PI OS Light and CoreElec 20 * Update year * Skip List working with Fedora38 Server (x86) and libreElec 11 (x86) * Update platform tag handling * Show error, if ssl lib cannot be found * Update supported platforms * Script to install selected Pull Requests * Fix misspelled explanation + improve description * Correct run-id evaluation * Support python3 and python2 * Support to copy existing config for PR testing --- .github/workflows/apt/amd64.json | 16 +- .github/workflows/apt/arm64.json | 8 - .github/workflows/apt/armhf.json | 16 -- .github/workflows/pull-request.yml | 10 +- .github/workflows/push-master.yml | 10 +- CMakeLists.txt | 21 +- Installation.md | 6 +- assets/webconfig/i18n/en.json | 4 +- bin/scripts/docker-compile.sh | 5 +- bin/scripts/install_pr.sh | 239 +++++++++++++++++++ cmake/Dependencies.cmake | 23 +- cmake/FindGitVersion.cmake | 2 +- debian/distributions | 13 +- doc/development/CompileHowto.md | 16 +- doc/development/SupportedPlatforms.md | 25 +- libsrc/cec/CMakeLists.txt | 1 + libsrc/hyperion/schema/schema-webConfig.json | 2 +- src/hyperiond/CMakeLists.txt | 2 +- 18 files changed, 310 insertions(+), 109 deletions(-) create mode 100755 bin/scripts/install_pr.sh diff --git a/.github/workflows/apt/amd64.json b/.github/workflows/apt/amd64.json index 7c794117..ecfa56c3 100644 --- a/.github/workflows/apt/amd64.json +++ b/.github/workflows/apt/amd64.json @@ -1,12 +1,4 @@ [ - { - "distribution": "Bionic", - "architecture": "amd64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev", - "package-depends": "libpython3.6, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg, libcec4", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 18.04 (Bionic Beaver) (amd64)" - }, { "distribution": "Focal", "architecture": "amd64", @@ -32,12 +24,12 @@ "description": "Ubuntu 22.10 (Kinetic Kudu) (amd64)" }, { - "distribution": "Stretch", + "distribution": "Lunar", "architecture": "amd64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev", - "package-depends": "libpython3.5, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg0, libcec4", + "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", + "package-depends": "libpython3.11, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Debian 9.x (Stretch) (amd64)" + "description": "Ubuntu 23.04 (Lunar Lobster) (amd64)" }, { "distribution": "Buster", diff --git a/.github/workflows/apt/arm64.json b/.github/workflows/apt/arm64.json index 5f0f233e..8cf6325e 100644 --- a/.github/workflows/apt/arm64.json +++ b/.github/workflows/apt/arm64.json @@ -1,12 +1,4 @@ [ - { - "distribution": "Bionic", - "architecture": "arm64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev", - "package-depends": "libpython3.6, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg, libcec4", - "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 18.04 (Bionic Beaver) (arm64)" - }, { "distribution": "Focal", "architecture": "arm64", diff --git a/.github/workflows/apt/armhf.json b/.github/workflows/apt/armhf.json index 66842969..ed4b9b4d 100644 --- a/.github/workflows/apt/armhf.json +++ b/.github/workflows/apt/armhf.json @@ -1,12 +1,4 @@ [ - { - "distribution": "Bionic", - "architecture": "armhf", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev", - "package-depends": "libpython3.6, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg, libcec4", - "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 18.04 (Bionic Beaver) (armhf)" - }, { "distribution": "Focal", "architecture": "armhf", @@ -31,14 +23,6 @@ "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", "description": "Ubuntu 22.10 (Kinetic Kudu) (armhf)" }, - { - "distribution": "Stretch", - "architecture": "armhf", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl1.0-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.5, libusb-1.0-0, libqt5widgets5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg0, libcec4", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description":"Debian 9.x (Stretch) (armhf)" - }, { "distribution": "Buster", "architecture": "armhf", diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 4aab0779..b7cd0bd3 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -18,16 +18,16 @@ jobs: dockerImage: [ x86_64, armv6l, armv7l, aarch64 ] include: - dockerImage: x86_64 - dockerName: Debian Stretch (x86_64) + dockerName: Debian Buster (x86_64) platform: x11 - dockerImage: armv6l - dockerName: Debian Stretch (Raspberry Pi v1 & ZERO) + dockerName: Debian Buster (Raspberry Pi v1 & ZERO) platform: rpi - dockerImage: armv7l - dockerName: Debian Stretch (Raspberry Pi 2 & 3) + dockerName: Debian Buster (Raspberry Pi 2 & 3) platform: rpi - dockerImage: aarch64 - dockerName: Debian Stretch (Generic AARCH64) + dockerName: Debian Buster (Generic AARCH64) platform: amlogic steps: @@ -47,7 +47,7 @@ jobs: - name: Build packages env: DOCKER_IMAGE: ${{ matrix.dockerImage }} - DOCKER_TAG: stretch + DOCKER_TAG: buster DOCKER_NAME: ${{ matrix.dockerName }} PLATFORM: ${{ matrix.platform }} shell: bash diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index dc290a5b..b5c575f3 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -20,16 +20,16 @@ jobs: dockerImage: [ x86_64, armv6l, armv7l, aarch64 ] include: - dockerImage: x86_64 - dockerName: Debian Stretch (x86_64) + dockerName: Debian Buster (x86_64) platform: x11 - dockerImage: armv6l - dockerName: Debian Stretch (Raspberry Pi v1 & ZERO) + dockerName: Debian Buster (Raspberry Pi v1 & ZERO) platform: rpi - dockerImage: armv7l - dockerName: Debian Stretch (Raspberry Pi 2 & 3) + dockerName: Debian Buster (Raspberry Pi 2 & 3) platform: rpi - dockerImage: aarch64 - dockerName: Debian Stretch (Generic AARCH64) + dockerName: Debian Buster (Generic AARCH64) platform: amlogic steps: @@ -41,7 +41,7 @@ jobs: - name: Build packages env: DOCKER_IMAGE: ${{ matrix.dockerImage }} - DOCKER_TAG: stretch + DOCKER_TAG: buster DOCKER_NAME: ${{ matrix.dockerName }} PLATFORM: ${{ matrix.platform }} shell: bash diff --git a/CMakeLists.txt b/CMakeLists.txt index e0c45882..cbb1ef3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,19 +180,18 @@ if ( "${PLATFORM}" MATCHES "osx" ) elseif ( "${PLATFORM}" MATCHES "rpi" ) SET ( DEFAULT_DISPMANX ON ) SET ( DEFAULT_DEV_WS281XPWM ON ) -elseif ( "${PLATFORM}" STREQUAL "amlogic" ) +elseif ( "${PLATFORM}" MATCHES "^amlogic" ) SET ( DEFAULT_AMLOGIC ON ) -elseif ( "${PLATFORM}" STREQUAL "amlogic-dev" ) - SET ( DEFAULT_AMLOGIC ON ) - SET ( DEFAULT_DISPMANX OFF ) - SET ( DEFAULT_QT OFF ) - SET ( DEFAULT_CEC OFF ) -elseif ( "${PLATFORM}" STREQUAL "amlogic64" ) - SET ( DEFAULT_AMLOGIC ON ) -elseif ( "${PLATFORM}" MATCHES "x11" ) + if ( "${PLATFORM}" MATCHES "-dev$" ) + SET ( DEFAULT_AMLOGIC ON ) + SET ( DEFAULT_DISPMANX OFF ) + SET ( DEFAULT_QT OFF ) + SET ( DEFAULT_CEC OFF ) + endif() +elseif ( "${PLATFORM}" MATCHES "^x11" ) SET ( DEFAULT_X11 ON ) SET ( DEFAULT_XCB ON ) - if ( "${PLATFORM}" STREQUAL "x11-dev" ) + if ( "${PLATFORM}" MATCHES "-dev$" ) SET ( DEFAULT_AMLOGIC ON) SET ( DEFAULT_DEV_WS281XPWM ON ) endif() @@ -201,7 +200,7 @@ elseif ( "${PLATFORM}" STREQUAL "imx6" ) endif() # enable tests for -dev builds -if ( "${PLATFORM}" MATCHES "-dev" ) +if ( "${PLATFORM}" MATCHES "-dev$" ) SET ( DEFAULT_TESTS ON ) endif() diff --git a/Installation.md b/Installation.md index 9f2fa84a..f55c1cdf 100644 --- a/Installation.md +++ b/Installation.md @@ -7,9 +7,9 @@ For Windows and macOS is an installation file available on our [Release page](ht ## Linux: On the following operating systems, Hyperion can currently be installed/updated using the method listed below: -- Raspbian Stretch/Raspberry Pi OS and later (armhf/arm64) -- Debian Stretch (9) and later (armhf/arm64/x86_64) -- Ubuntu 18.04 and later (armhf/arm64/x86_64) +- Raspbian Buster/Raspberry Pi OS and later (armhf/arm64) +- Debian Buster(10) and later (armhf/arm64/x86_64) +- Ubuntu 20.04 and later (armhf/arm64/x86_64) *** diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 6845685d..263c8bd6 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -560,7 +560,9 @@ "edt_conf_webc_keyPassPhrase_title": "Key password", "edt_conf_webc_keyPath_expl": "Path to the key file (format PEM, encrypted with RSA)", "edt_conf_webc_keyPath_title": "Private key path", - "edt_conf_webc_sslport_expl": "Port oft the HTTPS-Webserver", + "edt_conf_webc_port_expl": "Port for the WebServer, RPC and WebSocket HTTP connections", + "edt_conf_webc_port_title": "HTTP Port", + "edt_conf_webc_sslport_expl": "Port for the WebServer, RPC and WebSocket HTTPS connections", "edt_conf_webc_sslport_title": "HTTPS Port", "edt_dev_auth_key_title": "Authentication Token", "edt_dev_auth_key_title_info": "Authentication Token required to acccess the device", diff --git a/bin/scripts/docker-compile.sh b/bin/scripts/docker-compile.sh index a6abd11d..ce298b96 100755 --- a/bin/scripts/docker-compile.sh +++ b/bin/scripts/docker-compile.sh @@ -62,8 +62,7 @@ function printHelp { echo "######################################################## ## A script to compile Hyperion inside a docker container ## Requires installed Docker: https://www.docker.com/ -## Without arguments it will compile Hyperion for Debian Buster (x86_64) and uses Hyperion code from GitHub repository. -## Supports Raspberry Pi (armv6l, armv7l) cross compilation (Debian Stretch/Buster) and native compilation (Raspbian Stretch/Buster) +## Without arguments it will compile Hyperion for Debian Bullseye (x86_64) and uses Hyperion code from GitHub repository. ## For all images and tags currently available, see https://github.com/orgs/hyperion-project/packages ## ## Homepage: https://www.hyperion-project.org @@ -73,7 +72,7 @@ echo "######################################################## # # docker-compile.sh -h, --help # Show this help message # docker-compile.sh -i, --image # The docker image, e.g., x86_64, armv6l, armv7l, aarch64 -# docker-compile.sh -t, --tag # The docker tag, e.g., stretch, buster, bullseye, bookworm +# docker-compile.sh -t, --tag # The docker tag, e.g., buster, bullseye, bookworm # docker-compile.sh -b, --type # Release or Debug build # docker-compile.sh -p, --packages # If true, build packages with CPack # docker-compile.sh -l, --local # Run build using local code files diff --git a/bin/scripts/install_pr.sh b/bin/scripts/install_pr.sh new file mode 100755 index 00000000..ce029a08 --- /dev/null +++ b/bin/scripts/install_pr.sh @@ -0,0 +1,239 @@ +#!/bin/bash +# Script for downloading a specific open Pull Request Artifact from Hyperion.NG + +# Fixed variables +api_url="https://api.github.com/repos/hyperion-project/hyperion.ng" +type wget > /dev/null 2> /dev/null +hasWget=$? +type curl > /dev/null 2> /dev/null +hasCurl=$? +type python3 > /dev/null 2> /dev/null +hasPython3=$? +type python > /dev/null 2> /dev/null +hasPython2=$? + +if [[ "${hasWget}" -ne 0 ]] && [[ "${hasCurl}" -ne 0 ]]; then + echo '---> Critical Error: wget or curl required to download pull request artifacts' + exit 1 +fi + +if [[ "${hasPython3}" -eq 0 ]]; then + pythonCmd="python3" +else + if [[ "${hasPython2}" -eq 0 ]]; then + pythonCmd="python" + else + echo '---> Critical Error: python3 or python2 required to download pull request artifacts' + fi + exit 1 +fi + +function request_call() { + if [ $hasWget -eq 0 ]; then + echo $(wget --quiet --header="Authorization: token ${PR_TOKEN}" -O - $1) + elif [ $hasCurl -eq 0 ]; then + echo $(curl -skH "Authorization: token ${PR_TOKEN}" $1) + fi +} + +while getopts ":c:t:m:r:" opt; do + case "$opt" in + t) PR_TOKEN=$OPTARG ;; + r) run_id=$OPTARG ;; + m) ARCHITECTURE=$OPTARG ;; + c) CONFIGDIR=$OPTARG ;; + esac +done +shift $(( OPTIND - 1 )) + +# Check for a command line argument (PR number) +if [ "$1" == "" ] || [ $# -gt 1 ] || [ -z ${PR_TOKEN} ]; then + echo "Usage: $0 -t -m -r -c " >&2 + exit 1 +else + pr_number="$1" +fi + +# Set welcome message +echo '*******************************************************************************' +echo 'This script will download a specific open Pull Request Artifact from Hyperion.NG' +echo 'Created by hyperion-project.org - the official Hyperion source.' +echo '*******************************************************************************' + +# Determine the architecture, if not given +if [[ -z ${ARCHITECTURE} ]]; then + ARCHITECTURE=`uname -m` +fi + +#Test if multiarchitecture setup, i.e. user-space is 32bit +if [ ${ARCHITECTURE} == "aarch64" ]; then + USER_ARCHITECTURE=$ARCHITECTURE + IS_V7L=`cat /proc/$$/maps |grep -m1 -c v7l` + if [ $IS_V7L -ne 0 ]; then + USER_ARCHITECTURE="armv7l" + else + IS_V6L=`cat /proc/$$/maps |grep -m1 -c v6l` + if [ $IS_V6L -ne 0 ]; then + USER_ARCHITECTURE="armv6l" + fi + fi + if [ $ARCHITECTURE != $USER_ARCHITECTURE ]; then + echo "---> Identified kernel target architecture: $ARCHITECTURE" + echo "---> Identified user space target architecture: $USER_ARCHITECTURE" + ARCHITECTURE=$USER_ARCHITECTURE + fi +fi + +echo 'armv6l armv7l aarch64 x86_64' | grep -qw ${ARCHITECTURE} +if [ $? -ne 0 ]; then + echo "---> Critical Error: Target architecture $ARCHITECTURE is unknown -> abort" + exit 1 +else + echo "---> Download Pull Request for identified runtime architecture: $ARCHITECTURE" +fi + +# Determine if PR number exists +pulls=$(request_call "$api_url/pulls") + +pr_exists=$(echo "$pulls" | tr '\r\n' ' ' | ${pythonCmd} -c """ +import json,sys +data = json.load(sys.stdin) + +for i in data: + if i['number'] == "$pr_number": + print('exists') + break +""" 2>/dev/null) + +if [ "$pr_exists" != "exists" ]; then + echo "---> Pull Request $pr_number not found -> abort" + exit 1 +fi + +# Get head_sha value from 'pr_number' +head_sha=$(echo "$pulls" | tr '\r\n' ' ' | ${pythonCmd} -c """ +import json,sys +data = json.load(sys.stdin) + +for i in data: + if i['number'] == "$pr_number": + print(i['head']['sha']) + break +""" 2>/dev/null) + +if [ -z "$head_sha" ]; then + echo "---> The specified PR #$pr_number has no longer any artifacts." + echo "---> It may be older than 14 days. Ask the PR creator to recreate the artifacts at the following URL:" + echo "---> https://github.com/hyperion-project/hyperion.ng/pull/$pr_number" + exit 1 +fi + +if [ -z "$run_id" ]; then +# Determine run_id from head_sha +runs=$(request_call "$api_url/actions/runs") +run_id=$(echo "$runs" | tr '\r\n' ' ' | ${pythonCmd} -c """ +import json,sys +data = json.load(sys.stdin) + +for i in data['workflow_runs']: + if i['head_sha'] == '"$head_sha"': + print(i['id']) + break +""" 2>/dev/null) +fi + +if [ -z "$run_id" ]; then + echo "---> The specified PR #$pr_number has no longer any artifacts." + echo "---> It may be older than 14 days. Ask the PR creator to recreate the artifacts at the following URL:" + echo "---> https://github.com/hyperion-project/hyperion.ng/pull/$pr_number" + exit 1 +fi + +# Get archive_download_url from workflow +artifacts=$(request_call "$api_url/actions/runs/$run_id/artifacts") +archive_download_url=$(echo "$artifacts" | tr '\r\n' ' ' | ${pythonCmd} -c """ +import json,sys +data = json.load(sys.stdin) + +for i in data['artifacts']: + if i['name'] == '"$ARCHITECTURE"': + print(i['archive_download_url']) + break +""" 2>/dev/null) + +if [ -z "$archive_download_url" ]; then + echo "---> The specified PR #$pr_number has no longer any artifacts." + echo "---> It may be older than 14 days. Ask the PR creator to recreate the artifacts at the following URL:" + echo "---> https://github.com/hyperion-project/hyperion.ng/pull/$pr_number" + exit 1 +fi + +# Download packed PR artifact +echo "---> Downloading the Pull Request #$pr_number" +if [ $hasCurl -eq 0 ]; then + curl -skH "Authorization: token ${PR_TOKEN}" -o $HOME/temp.zip -L --get $archive_download_url +elif [ $hasWget -eq 0 ]; then + echo "wget" + wget --quiet --header="Authorization: token ${PR_TOKEN}" -O $HOME/temp.zip $archive_download_url +fi + +# Create new folder & extract PR artifact +echo "---> Extracting packed Artifact" +mkdir -p $HOME/hyperion_pr$pr_number +unzip -p $HOME/temp.zip | tar --strip-components=2 -C $HOME/hyperion_pr$pr_number share/hyperion/ -xz + +# Delete PR artifact +echo '---> Remove temporary files' +rm $HOME/temp.zip 2>/dev/null + +# Create the startup script +echo '---> Create startup script' +STARTUP_SCRIPT="#!/bin/bash -e + +# Stop hyperion service, if it is running +"'CURRENT_SERVICE=$(systemctl --type service | { grep -o "hyperion.*\.service" || true; }) +if [[ ! -z ${CURRENT_SERVICE} ]]; then + echo "---> Stop current service: ${CURRENT_SERVICE}" + + STOPCMD="systemctl stop --quiet ${CURRENT_SERVICE} --now" + USERNAME=${SUDO_USER:-$(whoami)} + if [ ${USERNAME} != "root" ]; then + STOPCMD="sudo ${STOPCMD}" + fi + + ${STOPCMD} >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "---> Critical Error: Failed to stop service: ${CURRENT_SERVICE}, Hyperion may not be started. Stop Hyperion manually." + else + echo "---> Service ${CURRENT_SERVICE} successfully stopped, Hyperion will be started" + fi +fi'"" + +TARGET_CONFIGDIR="$HOME/hyperion_pr$pr_number/config" + +if [[ ! -z ${CONFIGDIR} ]]; then +STARTUP_SCRIPT+=" +# Copy existing configuration file +"'echo "Copy existing configuration from "'${CONFIGDIR}" +mkdir -p "$TARGET_CONFIGDIR" +cp -ri "${CONFIGDIR}/*" "$TARGET_CONFIGDIR"" +fi + +STARTUP_SCRIPT+=" +# Start PR artifact +cd $HOME/hyperion_pr$pr_number +./bin/hyperiond -d -u $TARGET_CONFIGDIR" + +# Place startup script +echo "$STARTUP_SCRIPT" > $HOME/hyperion_pr$pr_number/$pr_number.sh + +# Set the executen bit +chmod +x -R $HOME/hyperion_pr$pr_number/$pr_number.sh + +echo "*******************************************************************************" +echo "Download finished!" +$REBOOTMESSAGE +echo "You can test the pull request with this command: ~/hyperion_pr$pr_number/$pr_number.sh" +echo "Remove the test installation with: rm -R ~/hyperion_pr$pr_number" +echo "Feedback is welcome at https://github.com/hyperion-project/hyperion.ng/pull/$pr_number" +echo "*******************************************************************************" diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index a727b0ad..08120831 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -132,28 +132,33 @@ macro(DeployLinux TARGET) include(GetPrerequisites) set(SYSTEM_LIBS_SKIP + "libatomic" "libc" + "libdbus" "libdl" "libexpat" "libfontconfig" - "libfreetype" "libgcc_s" "libgcrypt" - "libGL" - "libGLdispatch" + "libglib" "libglib-2" - "libGLX" "libgpg-error" + "liblz4" + "liblzma" "libm" + "libpcre" + "libpcre2" "libpthread" "librt" "libstdc++" + "libsystemd" "libudev" + "libusb" "libusb-1" "libutil" - "libX11" + "libuuid" "libz" - ) + ) if (ENABLE_DISPMANX) list(APPEND SYSTEM_LIBS_SKIP "libcec") @@ -161,7 +166,9 @@ macro(DeployLinux TARGET) # Extract dependencies ignoring the system ones get_prerequisites(${TARGET_FILE} DEPENDENCIES 0 1 "" "") - + + message(STATUS "Dependencies for target file: ${DEPENDENCIES}") + # Append symlink and non-symlink dependencies to the list set(PREREQUISITE_LIBS "") foreach(DEPENDENCY ${DEPENDENCIES}) @@ -203,6 +210,8 @@ macro(DeployLinux TARGET) get_filename_component(file_canonical ${openssl_lib} REALPATH) gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) endforeach() + else() + message( WARNING "OpenSSL NOT found (https webserver will not work)") endif(OPENSSL_FOUND) # Detect the Qt plugin directory, source: https://github.com/lxde/lxqt-qtplugin/blob/master/src/CMakeLists.txt diff --git a/cmake/FindGitVersion.cmake b/cmake/FindGitVersion.cmake index d0bcf494..4e8db794 100644 --- a/cmake/FindGitVersion.cmake +++ b/cmake/FindGitVersion.cmake @@ -1,4 +1,4 @@ - +execute_process( COMMAND git config --global --add safe.directory ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ERROR_QUIET ) execute_process( COMMAND git log -1 --format=%cn-%t/%h-%ct WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE BUILD_ID ERROR_QUIET ) execute_process( COMMAND sh -c "git branch | grep '^*' | sed 's;^*;;g' " WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE VERSION_ID ERROR_QUIET ) execute_process( COMMAND sh -c "git remote --verbose | grep origin | grep fetch | cut -f2 | cut -d' ' -f1" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_REMOTE_PATH ERROR_QUIET ) diff --git a/debian/distributions b/debian/distributions index 214d10e2..12da7848 100644 --- a/debian/distributions +++ b/debian/distributions @@ -1,11 +1,3 @@ -Origin: Hyperion-Project -Label: apt.hyperion-project.org -Codename: bionic -Architectures: amd64 armhf arm64 -Components: main -Description: Official APT Repository by Hyperion Project -SignWith: yes - Origin: Hyperion-Project Label: apt.hyperion-project.org Codename: focal @@ -32,9 +24,8 @@ SignWith: yes Origin: Hyperion-Project Label: apt.hyperion-project.org -Suite: oldoldstable -Codename: stretch -Architectures: armhf amd64 +Codename: lunar +Architectures: amd64 Components: main Description: Official APT Repository by Hyperion Project SignWith: yes diff --git a/doc/development/CompileHowto.md b/doc/development/CompileHowto.md index 8a2cc7df..f50c1878 100644 --- a/doc/development/CompileHowto.md +++ b/doc/development/CompileHowto.md @@ -1,16 +1,12 @@ # With Docker -If you are using [Docker](https://www.docker.com/), you can compile Hyperion inside a docker container. This keeps your system clean and with a simple script it's easy to use. Supported is also cross compiling for Raspberry Pi (Debian Stretch or higher). To compile Hyperion just execute one of the following commands. +If you are using [Docker](https://www.docker.com/), you can compile Hyperion inside a docker container. This keeps your system clean and with a simple script it's easy to use. Supported is also cross compiling for Raspberry Pi (Debian Buster or higher). To compile Hyperion just execute one of the following commands. The compiled binaries and packages will be available at the deploy folder next to the script.
Note: call the script with `./docker-compile.sh -h` for more options. ## Cross compilation on x86_64 for: -**x86_64 (Debian Stretch):** -```console -wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i x86_64 -t stretch -``` **x86_64 (Debian Buster):** ```console wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i x86_64 -t buster @@ -23,10 +19,6 @@ wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/ ```console wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i x86_64 -t bookworm ``` -**Raspberry Pi v1 & ZERO (Debian Stretch)** -```console -wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i armv6l -t stretch -``` **Raspberry Pi v1 & ZERO (Debian Buster)** ```console wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i armv6l -t buster @@ -39,10 +31,6 @@ wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/ ```console wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i armv6l -t bookworm ``` -**Raspberry Pi 2/3/4 (Debian Stretch)** -```console -wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i armv7l -t stretch -``` **Raspberry Pi 2/3/4 (Debian Buster)** ```console wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -i armv7l -t buster @@ -78,7 +66,7 @@ sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev ```console sudo apt-get update -sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config +sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libxkbcommon-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config ``` **For Linux X11/XCB grabber support** diff --git a/doc/development/SupportedPlatforms.md b/doc/development/SupportedPlatforms.md index 2e170c57..39e812c4 100644 --- a/doc/development/SupportedPlatforms.md +++ b/doc/development/SupportedPlatforms.md @@ -7,12 +7,12 @@ In case of problems, it is recommended checking with the wider Hyperion communit | Hardware | OS | Version | Screen-Grabber | Package | Comments | |-----------|-----------------|--------------------|-----------------------------------------|-------------------------------------------------------------------------------|------------------------------------| | X64 | Windows | 10 | QT¹ | [Windows-AMD64.exe](https://github.com/hyperion-project/hyperion.ng/releases) | Direct X9 Grabber via self-compile | -| X64 | Ubuntu | 18.04, 20.04, 22.04² | QT¹
XCB/X11¹ | [Linux-x86_64.deb](https://github.com/hyperion-project/hyperion.ng/releases) | | -| X64 | Debian | 9, 10, 11, 12³| QT¹
XCB/X11¹ | [Linux-x86_64.deb](https://github.com/hyperion-project/hyperion.ng/releases) | | -| RPi 4 | HyperBian | 9, 10, 11, 12³| QT¹
XCB/X11¹
DispmanX | [HyperBian.zip](https://github.com/Hyperion-Project/HyperBian/releases) | | -| RPi 4 | Raspberry Pi OS | 9, 10, 11, 12³| QT¹
XCB/X11¹
DispmanX | [Linux-armv7l.deb](https://github.com/hyperion-project/hyperion.ng/releases) | | -| RPi 3 /3+ | HyperBian | 9, 10, 11, 12³| QT¹
XCB/X11¹
DispmanX | [HyperBian.zip](https://github.com/hyperion-project/hyperion.ng/releases) | | -| RPi 3 /3+ | Raspberry Pi OS | 9, 10, 11, 12³| QT¹
XCB/X11¹
DispmanX | [Linux-armv7l.deb](https://github.com/hyperion-project/hyperion.ng/releases) | | +| X64 | Ubuntu | 20.04, 22.04, 23.04² | QT¹
XCB/X11¹ | [Linux-x86_64.deb](https://github.com/hyperion-project/hyperion.ng/releases) | | +| X64 | Debian | 10, 11, 12³ | QT¹
XCB/X11¹ | [Linux-x86_64.deb](https://github.com/hyperion-project/hyperion.ng/releases) | | +| RPi 4 | HyperBian | 10, 11, 12³ | QT¹
XCB/X11¹
DispmanX | [HyperBian.zip](https://github.com/Hyperion-Project/HyperBian/releases) | | +| RPi 4 | Raspberry Pi OS | 10, 11, 12³ | QT¹
XCB/X11¹
DispmanX | [Linux-armv7l.deb](https://github.com/hyperion-project/hyperion.ng/releases) | | +| RPi 3 /3+ | HyperBian | 10, 11, 12³ | QT¹
XCB/X11¹
DispmanX | [HyperBian.zip](https://github.com/hyperion-project/hyperion.ng/releases) | | +| RPi 3 /3+ | Raspberry Pi OS | 10, 11, 12³ | QT¹
XCB/X11¹
DispmanX | [Linux-armv7l.deb](https://github.com/hyperion-project/hyperion.ng/releases) | | ## Unofficial In case you have an additional working setups you would like to share with the community, please get in touch or issue a PR to have the table updated. @@ -20,13 +20,18 @@ In case you have an additional working setups you would like to share with the c | Hardware | OS | Version | Screen-Grabber | Package | Comments | |---------------|-----------------|----------------|-----------------------------------------|---------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------| | X64 | macOS | 11, 12 | QT
OSX | [macOS-x86_64.tar.gz](https://github.com/hyperion-project/hyperion.ng/releases) | M1 not tested | -| X64 | Fedora | 35 | QT¹
XCB/X11¹ | [Linux-x86_64.rpm](https://github.com/hyperion-project/hyperion.ng/releases) | | +| X64 | Fedora | 38 | QT¹
XCB/X11¹ | [Linux-x86_64.rpm](https://github.com/hyperion-project/hyperion.ng/releases) | | | X64 | Arch | | QT¹
XCB/X11¹ | [Linux-x86_64.rpm](https://github.com/hyperion-project/hyperion.ng/releases) | | -| RPi 0/ 1 / 2 | Raspberry Pi OS | 9, 10, 11, 12³| QT¹
XCB/X11¹
DispmanX | [Linux-armv6l.tar.gz](https://github.com/hyperion-project/hyperion.ng/releases) | No recommended | +| RPi 0/ 1 / 2 | Raspberry Pi OS | 10, 11, 12³| QT¹
XCB/X11¹
DispmanX | [Linux-armv6l.tar.gz](https://github.com/hyperion-project/hyperion.ng/releases) | No recommended | +| X64 | LibreElec | 11.x (Nexus) | [Kodi add-on](https://github.com/hyperion-project/hyperion.kodi/releases) | [Linux-x86_64.tar.gz](https://github.com/hyperion-project/hyperion.ng/releases) | [Install on LibreELEC](https://hyperion-project.org/forum/index.php?thread/10463-install-hyperion-ng-on-libreelec-x86-64-rpi-inoffiziell-unofficially/) | +| RPi 4 | LibreElec | 11.x (Nexus) | - | [Linux-armv7l.tar.gz](https://github.com/hyperion-project/hyperion.ng/releases) | [Install on LibreELEC](https://hyperion-project.org/forum/index.php?thread/10463-install-hyperion-ng-on-libreelec-x86-64-rpi-inoffiziell-unofficially/) | | RPi 4 | LibreElec | 10.x (Matrix) | - | [Linux-armv7l.tar.gz](https://github.com/hyperion-project/hyperion.ng/releases) | [Install on LibreELEC](https://hyperion-project.org/forum/index.php?thread/10463-install-hyperion-ng-on-libreelec-x86-64-rpi-inoffiziell-unofficially/) | | RPi 4 | LibreElec | 9.2.x (Leia) | QT¹
DispmanX | [Linux-armv7l.tar.gz](https://github.com/hyperion-project/hyperion.ng/releases) | [Install on LibreELEC](https://hyperion-project.org/forum/index.php?thread/10463-install-hyperion-ng-on-libreelec-x86-64-rpi-inoffiziell-unofficially/) | +| RPi 3 /3+ | LibreElec | 11.x (Nexus) | - | [Linux-armv7l.tar.gz](https://github.com/hyperion-project/hyperion.ng/releases) | [Install on LibreELEC](https://hyperion-project.org/forum/index.php?thread/10463-install-hyperion-ng-on-libreelec-x86-64-rpi-inoffiziell-unofficially/) | | RPi 3 /3+ | LibreElec | 10.x (Matrix) | - | [Linux-armv7l.tar.gz](https://github.com/hyperion-project/hyperion.ng/releases) | [Install on LibreELEC](https://hyperion-project.org/forum/index.php?thread/10463-install-hyperion-ng-on-libreelec-x86-64-rpi-inoffiziell-unofficially/) | | RPi 3 /3+ | LibreElec | 9.2.x (Leia) | QT¹
DispmanX | [Linux-armv7l.tar.gz](https://github.com/hyperion-project/hyperion.ng/releases) | [Install on LibreELEC](https://hyperion-project.org/forum/index.php?thread/10463-install-hyperion-ng-on-libreelec-x86-64-rpi-inoffiziell-unofficially/) | +| Amlogic | CoreElec | 21.x (Omega) | Amlogic | CoreElec Plugin | Supported via CoreElec project | +| Amlogic | CoreElec | 20.x (Nexus) | Amlogic | CoreElec Plugin | Supported via CoreElec project | | Amlogic | CoreElec | 19.x (Matrix) | Amlogic | CoreElec Plugin | Supported via CoreElec project | | Amlogic | CoreElec | 9.2.x (Leia) | Amlogic | CoreElec Plugin | Supported via CoreElec project | | Vero4K | OSMC | | | | [hyperion-vero4k](https://github.com/hissingshark/hyperion-vero4k) | @@ -35,5 +40,5 @@ In case you have an additional working setups you would like to share with the c Legend --- ¹ Requires an environment with `DISPLAY` defined\ -² 18=Bionic Beaver, 20=Focal Fossa, 22=Jammy Jellyfish\ -³ 9=Stretch, 10=Buster, 11=Bullseye, 12=Bookworm +² 20=Focal Fossa, 22=Jammy Jellyfish, 23=Lunar Lobster\ +³ 10=Buster, 11=Bullseye, 12=Bookworm diff --git a/libsrc/cec/CMakeLists.txt b/libsrc/cec/CMakeLists.txt index 00a1497b..dfd80e7d 100644 --- a/libsrc/cec/CMakeLists.txt +++ b/libsrc/cec/CMakeLists.txt @@ -7,6 +7,7 @@ SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/cec) FILE (GLOB CEC_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp") add_library(cechandler ${CEC_SOURCES}) +list(GET CEC_LIBRARIES 0 CEC_LIBRARIES) add_definitions(-DCEC_LIBRARY="${CEC_LIBRARIES}") include_directories(${CEC_INCLUDE_DIRS}) diff --git a/libsrc/hyperion/schema/schema-webConfig.json b/libsrc/hyperion/schema/schema-webConfig.json index 17d9e60d..6101dc76 100644 --- a/libsrc/hyperion/schema/schema-webConfig.json +++ b/libsrc/hyperion/schema/schema-webConfig.json @@ -13,7 +13,7 @@ "port" : { "type" : "integer", - "title" : "edt_conf_general_port_title", + "title" : "edt_conf_webc_port_title", "minimum" : 80, "maximum" : 65535, "default" : 8090, diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index bc38505b..35326c22 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -160,7 +160,7 @@ if (APPLE) MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/osxbundle/Info.plist.in MACOSX_BUNDLE_BUNDLE_NAME "Hyperion" MACOSX_BUNDLE_BUNDLE_VERSION ${HYPERION_VERSION} - MACOSX_BUNDLE_COPYRIGHT "Copyright(c) 2014-2022 Hyperion Project" + MACOSX_BUNDLE_COPYRIGHT "Copyright(c) 2014-2023 Hyperion Project" MACOSX_BUNDLE_GUI_IDENTIFIER "com.hyperion-project.${PROJECT_NAME}" MACOSX_BUNDLE_ICON_FILE "Hyperion.icns" MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${HYPERION_VERSION}" From 1257cfff70c50aadb8b26c45619a0f9e4a8c1652 Mon Sep 17 00:00:00 2001 From: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com> Date: Fri, 21 Jul 2023 18:59:23 +0000 Subject: [PATCH 18/98] Set OpenSSL to 1.1.1 (Windows) --- .github/workflows/pull-request.yml | 5 +++-- .github/workflows/push-master.yml | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index b7cd0bd3..ef37014c 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -150,7 +150,7 @@ jobs: with: path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey key: ${{ runner.os }}-chocolatey - + # - name: Install Python # shell: powershell # run: | @@ -159,7 +159,8 @@ jobs: - name: Install OpenSSL, DirectX SDK shell: powershell run: | - choco install --no-progress openssl directx-sdk -y + choco install --no-progress openssl --version=1.1.1.2100 -y + choco install --no-progress directx-sdk -y - name: Install libjpeg-turbo run: | diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index b5c575f3..1edd8594 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -124,7 +124,8 @@ jobs: - name: Install OpenSSL, DirectX SDK shell: powershell run: | - choco install --no-progress openssl directx-sdk -y + choco install --no-progress openssl --version=1.1.1.2100 -y + choco install --no-progress directx-sdk -y - name: Install libjpeg-turbo run: | From da275dd44861e1c2ce3501ffb6eba7300e1446ee Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Sat, 29 Jul 2023 19:28:28 +0200 Subject: [PATCH 19/98] Update flatbuffers to v23.5.26 (#1624) --- dependencies/external/flatbuffers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/external/flatbuffers b/dependencies/external/flatbuffers index 8468eab8..0100f6a5 160000 --- a/dependencies/external/flatbuffers +++ b/dependencies/external/flatbuffers @@ -1 +1 @@ -Subproject commit 8468eab83bacc8bbd6cb5ae22197af06a9437b2d +Subproject commit 0100f6a5779831fa7a651e4b67ef389a8752bd9b From 5bf25c98ada6cb41a5c0ab4c9cc2c7b8ff73bbbe Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Sat, 29 Jul 2023 19:28:39 +0200 Subject: [PATCH 20/98] Update mbedTLS to v3.4.0 (#1625) --- dependencies/external/mbedtls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/external/mbedtls b/dependencies/external/mbedtls index 8c892249..1873d3bf 160000 --- a/dependencies/external/mbedtls +++ b/dependencies/external/mbedtls @@ -1 +1 @@ -Subproject commit 8c89224991adff88d53cd380f42a2baa36f91454 +Subproject commit 1873d3bfc2da771672bd8e7e8f41f57e0af77f33 From c0dc08b0c07e22f76ea2c88f5e3e18078ebcdc97 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Sat, 29 Jul 2023 19:28:51 +0200 Subject: [PATCH 21/98] Update to Protobuf 23.4.0 (#1626) * Update to protobuf v23.4.0 * Add defines for 3rd party sub-modules used by protobuf * Check out sub-modules recursively --- .github/workflows/nightly.yml | 2 +- .github/workflows/pull-request.yml | 6 +++--- .github/workflows/push-master.yml | 6 +++--- dependencies/CMakeLists.txt | 4 ++++ dependencies/external/protobuf | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 8a4da1e2..7151ce2f 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -86,7 +86,7 @@ jobs: steps: - uses: actions/checkout@v3 with: - submodules: true + submodules: recursive - name: Generate environment variables run: | diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index ef37014c..47769bfb 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -34,7 +34,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - submodules: true + submodules: recursive # Append PR number to .version - name: Append PR number to version @@ -78,7 +78,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - submodules: true + submodules: recursive # Append PR number to .version - name: Append PR number to version @@ -127,7 +127,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - submodules: true + submodules: recursive # Append PR number to .version - name: Append PR number to version diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index 1edd8594..8f1b44c3 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -35,7 +35,7 @@ jobs: steps: - uses: actions/checkout@v3 with: - submodules: true + submodules: recursive # Build process - name: Build packages @@ -64,7 +64,7 @@ jobs: steps: - uses: actions/checkout@v3 with: - submodules: true + submodules: recursive # Install dependencies - name: Install dependencies @@ -99,7 +99,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - submodules: true + submodules: recursive - name: Install Qt uses: jurplel/install-qt-action@v3 diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 858454ee..c13e96cc 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -160,6 +160,10 @@ if(ENABLE_PROTOBUF_SERVER) # define the protobuf library set(PROTOBUF_LIBRARIES protobuf::libprotobuf) + + # defines for 3rd party sub-modules + set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "Build abseil-cpp with C++ version requirements propagated") + endif() # redefine at parent scope diff --git a/dependencies/external/protobuf b/dependencies/external/protobuf index f0dc78d7..2c5fa078 160000 --- a/dependencies/external/protobuf +++ b/dependencies/external/protobuf @@ -1 +1 @@ -Subproject commit f0dc78d7e6e331b8c6bb2d5283e06aa26883ca7c +Subproject commit 2c5fa078d8e86e5f4bd34e6f4c9ea9e8d7d4d44a From d1879c2e393c1e6882de9dd7f04349e4551a6579 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Thu, 3 Aug 2023 22:40:25 +0200 Subject: [PATCH 22/98] ws281x - Update logic to identify is user is admin (#1623) --- assets/webconfig/js/content_leds.js | 2 +- libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js index 2a95af81..1aec94ab 100755 --- a/assets/webconfig/js/content_leds.js +++ b/assets/webconfig/js/content_leds.js @@ -2069,7 +2069,7 @@ var updateOutputSelectList = function (ledType, discoveryInfo) { case "devRPiPWM": key = ledType; - if (discoveryInfo.devices.length == 0) { + if (!discoveryInfo.isUserAdmin) { enumVals.push("NONE"); enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none')); $('#btn_submit_controller').prop('disabled', true); diff --git a/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp b/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp index 2b67173c..af72304a 100644 --- a/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp +++ b/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp @@ -159,12 +159,10 @@ QJsonObject LedDeviceWS281x::discover(const QJsonObject& /*params*/) QJsonArray deviceList; - if (SysInfo::isUserAdmin()) - { - //Indicate the general availability of the device, if hyperion is run under root - deviceList << QJsonObject ({{"found",true}}); - devicesDiscovered.insert("devices", deviceList); - } + //Indicate the general availability of the device, if hyperion is run under root + devicesDiscovered.insert("isUserAdmin", SysInfo::isUserAdmin()); + + devicesDiscovered.insert("devices", deviceList); DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData()); From a5bb7e905ba153140c6563e88937e121ae3a88a9 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:12:43 +0200 Subject: [PATCH 23/98] Fix non image updates ignore blacklisted leds (#1635) * Fix #1634 * Refactor to process blacklisted LEDs with less resources --- include/hyperion/LedString.h | 48 ++++++++++++++++----- include/utils/hyperion.h | 38 ----------------- libsrc/hyperion/Hyperion.cpp | 18 ++++++-- libsrc/hyperion/LedString.cpp | 80 ++++++++++++++++++++++++++++++++++- test/TestImage2LedsMap.cpp | 2 +- 5 files changed, 132 insertions(+), 54 deletions(-) diff --git a/include/hyperion/LedString.h b/include/hyperion/LedString.h index 282b4429..6a5bfc52 100644 --- a/include/hyperion/LedString.h +++ b/include/hyperion/LedString.h @@ -10,6 +10,7 @@ // QT includes #include +#include // Forward class declarations namespace Json { class Value; } @@ -73,7 +74,7 @@ inline ColorOrder stringToColorOrder(const QString & order) } /// -/// The Led structure contains the definition of the image portion used to determine a single led's +/// The Led structure contains the definition of the image portion used to determine a single LED's /// color. /// @verbatim /// |--------------------image--| @@ -89,39 +90,66 @@ inline ColorOrder stringToColorOrder(const QString & order) /// struct Led { - /// The minimum vertical scan line included for this leds color + /// The minimum vertical scan line included for this LEDs color double minX_frac; - /// The maximum vertical scan line included for this leds color + /// The maximum vertical scan line included for this LEDs color double maxX_frac; - /// The minimum horizontal scan line included for this leds color + /// The minimum horizontal scan line included for this LEDs color double minY_frac; - /// The maximum horizontal scan line included for this leds color + /// The maximum horizontal scan line included for this LEDs color double maxY_frac; + /// A LEDs at {0,0,0,0} is not visible and therefore treated as blacklisted + bool isBlacklisted {false}; /// the color order ColorOrder colorOrder; }; /// -/// The LedString contains the image integration information of the leds +/// The LedString contains the image integration information of the LEDs /// class LedString { public: /// - /// Returns the led specifications + /// Returns the LED specifications /// /// @return The list with led specifications /// std::vector& leds(); /// - /// Returns the led specifications + /// Returns the LED specifications /// /// @return The list with led specifications /// const std::vector& leds() const; + /// + /// Returns the IDs of blacklisted LEDs + /// + /// @return ID List of blacklisted LEDs + /// + std::vector& blacklistedLedIds(); + + /// + /// Returns the IDs of blacklisted LEDs + /// + /// @return ID List of blacklisted LEDs + /// + const std::vector& blacklistedLedIds() const; + + /// + /// Check, if teh layout has blacklisted LEDs configured + /// + /// @return True, if blacklisted LEDs are configured + /// + bool hasBlackListedLeds (); + + static LedString createLedString(const QJsonArray& ledConfigArray, const ColorOrder deviceOrder); + private: - /// The list with led specifications - std::vector mLeds; + /// The list with LED specifications + std::vector _leds; + /// The list containing IDs of blacklisted LED + std::vector _blacklistedLedIds; }; diff --git a/include/utils/hyperion.h b/include/utils/hyperion.h index 774267a8..88ebc44e 100644 --- a/include/utils/hyperion.h +++ b/include/utils/hyperion.h @@ -177,44 +177,6 @@ namespace hyperion { return adjustment; } - /** - * Construct the 'led-string' with the integration area definition per led and the color - * ordering of the RGB channels - * @param ledsConfig The configuration of the led areas - * @param deviceOrder The default RGB channel ordering - * @return The constructed ledstring - */ - static LedString createLedString(const QJsonArray& ledConfigArray, const ColorOrder deviceOrder) - { - LedString ledString; - const QString deviceOrderStr = colorOrderToString(deviceOrder); - - for (signed i = 0; i < ledConfigArray.size(); ++i) - { - const QJsonObject& ledConfig = ledConfigArray[i].toObject(); - Led led; - - led.minX_frac = qMax(0.0, qMin(1.0, ledConfig["hmin"].toDouble())); - led.maxX_frac = qMax(0.0, qMin(1.0, ledConfig["hmax"].toDouble())); - led.minY_frac = qMax(0.0, qMin(1.0, ledConfig["vmin"].toDouble())); - led.maxY_frac = qMax(0.0, qMin(1.0, ledConfig["vmax"].toDouble())); - // Fix if the user swapped min and max - if (led.minX_frac > led.maxX_frac) - { - std::swap(led.minX_frac, led.maxX_frac); - } - if (led.minY_frac > led.maxY_frac) - { - std::swap(led.minY_frac, led.maxY_frac); - } - - // Get the order of the rgb channels for this led (default is device order) - led.colorOrder = stringToColorOrder(ledConfig["colorOrder"].toString(deviceOrderStr)); - ledString.leds().push_back(led); - } - return ledString; - } - static QSize getLedLayoutGridSize(const QJsonArray& ledConfigArray) { std::vector midPointsX; diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 7107611e..c0441294 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -52,7 +52,7 @@ Hyperion::Hyperion(quint8 instance, bool readonlyMode) , _instIndex(instance) , _settingsManager(new SettingsManager(instance, this, readonlyMode)) , _componentRegister(nullptr) - , _ledString(hyperion::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object()))) + , _ledString(LedString::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object()))) , _imageProcessor(nullptr) , _muxer(nullptr) , _raw2ledAdjustment(hyperion::createLedColorsAdjustment(static_cast(_ledString.leds().size()), getSetting(settings::COLOR).object())) @@ -255,7 +255,7 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co #endif // ledstring, img processor, muxer, ledGridSize (effect-engine image based effects), _ledBuffer and ByteOrder of ledstring - _ledString = hyperion::createLedString(leds, hyperion::createColorOrder(getSetting(settings::DEVICE).object())); + _ledString = LedString::createLedString(leds, hyperion::createColorOrder(getSetting(settings::DEVICE).object())); _imageProcessor->setLedString(_ledString); _muxer->updateLedColorsLength(static_cast(_ledString.leds().size())); _ledGridSize = hyperion::getLedLayoutGridSize(leds); @@ -291,7 +291,7 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co // force ledString update, if device ByteOrder changed if(_ledDeviceWrapper->getColorOrder() != dev["colorOrder"].toString("rgb")) { - _ledString = hyperion::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(dev)); + _ledString = LedString::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(dev)); _imageProcessor->setLedString(_ledString); _ledStringColorOrder.clear(); @@ -671,6 +671,18 @@ void Hyperion::update() else { _ledBuffer = priorityInfo.ledColors; + + if (_ledString.hasBlackListedLeds()) + { + for (int id : _ledString.blacklistedLedIds()) + { + if (id > _ledBuffer.size()-1) + { + break; + } + _ledBuffer.at(id) = ColorRgb::BLACK; + } + } } // emit rawLedColors before transform diff --git a/libsrc/hyperion/LedString.cpp b/libsrc/hyperion/LedString.cpp index bf9f7528..172504c4 100644 --- a/libsrc/hyperion/LedString.cpp +++ b/libsrc/hyperion/LedString.cpp @@ -1,16 +1,92 @@ // STL includes + #include #include // hyperion includes #include +// QT includes +#include + std::vector& LedString::leds() { - return mLeds; + return _leds; } const std::vector& LedString::leds() const { - return mLeds; + return _leds; +} + +std::vector& LedString::blacklistedLedIds() +{ + return _blacklistedLedIds; +} + +const std::vector& LedString::blacklistedLedIds() const +{ + return _blacklistedLedIds; +} + +bool LedString::hasBlackListedLeds() +{ + + if (_blacklistedLedIds.size() > 0) + { + return true; + } + else + { + return false; + } +} + +/** + * Construct the 'led-string' with the integration area definition per led and the color + * ordering of the RGB channels + * @param ledsConfig The configuration of the led areas + * @param deviceOrder The default RGB channel ordering + * @return The constructed ledstring + */ +LedString LedString::createLedString(const QJsonArray& ledConfigArray, const ColorOrder deviceOrder) +{ + LedString ledString; + const QString deviceOrderStr = colorOrderToString(deviceOrder); + + for (signed i = 0; i < ledConfigArray.size(); ++i) + { + const QJsonObject& ledConfig = ledConfigArray[i].toObject(); + Led led; + + led.minX_frac = qMax(0.0, qMin(1.0, ledConfig["hmin"].toDouble())); + led.maxX_frac = qMax(0.0, qMin(1.0, ledConfig["hmax"].toDouble())); + led.minY_frac = qMax(0.0, qMin(1.0, ledConfig["vmin"].toDouble())); + led.maxY_frac = qMax(0.0, qMin(1.0, ledConfig["vmax"].toDouble())); + // Fix if the user swapped min and max + if (led.minX_frac > led.maxX_frac) + { + std::swap(led.minX_frac, led.maxX_frac); + } + if (led.minY_frac > led.maxY_frac) + { + std::swap(led.minY_frac, led.maxY_frac); + } + + // Get the order of the rgb channels for this led (default is device order) + led.colorOrder = stringToColorOrder(ledConfig["colorOrder"].toString(deviceOrderStr)); + + led.isBlacklisted = false; + if (led.minX_frac < std::numeric_limits::epsilon() && + led.maxX_frac < std::numeric_limits::epsilon() && + led.minY_frac < std::numeric_limits::epsilon() && + led.maxY_frac < std::numeric_limits::epsilon() + ) + { + led.isBlacklisted = true; + ledString.blacklistedLedIds().push_back(i); + } + ledString.leds().push_back(led); + } + return ledString; } diff --git a/test/TestImage2LedsMap.cpp b/test/TestImage2LedsMap.cpp index bb9aa618..c0f08430 100644 --- a/test/TestImage2LedsMap.cpp +++ b/test/TestImage2LedsMap.cpp @@ -24,7 +24,7 @@ int main() return -1; } - const LedString ledString = hyperion::createLedString(config["leds"].toArray(), hyperion::createColorOrder(config["device"].toObject())); + const LedString ledString = LedString::createLedString(config["leds"].toArray(), hyperion::createColorOrder(config["device"].toObject())); const ColorRgb testColor = {64, 123, 12}; From 7909997398ff4177f660e26db0c96677a199b69d Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Wed, 6 Sep 2023 20:08:20 +0200 Subject: [PATCH 24/98] Update install_pr.sh Only run named 'Hyperion PR Build' have artifacts --- bin/scripts/install_pr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/scripts/install_pr.sh b/bin/scripts/install_pr.sh index ce029a08..8549f797 100755 --- a/bin/scripts/install_pr.sh +++ b/bin/scripts/install_pr.sh @@ -136,7 +136,7 @@ import json,sys data = json.load(sys.stdin) for i in data['workflow_runs']: - if i['head_sha'] == '"$head_sha"': + if i['head_sha'] == '"$head_sha"' and i['name'] == 'Hyperion PR Build': print(i['id']) break """ 2>/dev/null) From 48cea4ad9b1bf3ea40e2ac5c8465639f9f08565c Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Sat, 30 Sep 2023 18:00:38 +0200 Subject: [PATCH 25/98] Focus on relevant PRs --- bin/scripts/install_pr.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/scripts/install_pr.sh b/bin/scripts/install_pr.sh index 8549f797..bab790a0 100755 --- a/bin/scripts/install_pr.sh +++ b/bin/scripts/install_pr.sh @@ -93,7 +93,7 @@ else fi # Determine if PR number exists -pulls=$(request_call "$api_url/pulls") +pulls=$(request_call "$api_url/pulls?state=open") pr_exists=$(echo "$pulls" | tr '\r\n' ' ' | ${pythonCmd} -c """ import json,sys @@ -106,7 +106,7 @@ for i in data: """ 2>/dev/null) if [ "$pr_exists" != "exists" ]; then - echo "---> Pull Request $pr_number not found -> abort" + echo "---> Pull Request $pr_number not found as open PR -> abort" exit 1 fi @@ -122,7 +122,7 @@ for i in data: """ 2>/dev/null) if [ -z "$head_sha" ]; then - echo "---> The specified PR #$pr_number has no longer any artifacts." + echo "---> The specified PR #$pr_number has no longer any artifacts or has been closed." echo "---> It may be older than 14 days. Ask the PR creator to recreate the artifacts at the following URL:" echo "---> https://github.com/hyperion-project/hyperion.ng/pull/$pr_number" exit 1 @@ -130,13 +130,13 @@ fi if [ -z "$run_id" ]; then # Determine run_id from head_sha -runs=$(request_call "$api_url/actions/runs") +runs=$(request_call "$api_url/actions/runs?head_sha=$head_sha") run_id=$(echo "$runs" | tr '\r\n' ' ' | ${pythonCmd} -c """ import json,sys data = json.load(sys.stdin) for i in data['workflow_runs']: - if i['head_sha'] == '"$head_sha"' and i['name'] == 'Hyperion PR Build': + if i['name'] == 'Hyperion PR Build': print(i['id']) break """ 2>/dev/null) From 08dc59c88561baa7dae422187060489c9b6b6f02 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Sun, 1 Oct 2023 21:56:53 +0200 Subject: [PATCH 26/98] Fix #1630 - Audio Capture settings are ignored (#1640) * Fix macOS build * Update minimum cmake version * Correct compile errorswith Qt6.7 * Update minimum cmake version (2) * Use C++17 * Correct compile errors with Qt6.7 * Replace unsupported Lambda UniqueConnection * Support UTF-8 Output on console * Fix #1630 --- CMakeLists.txt | 34 +++++++------- assets/webconfig/js/content_grabber.js | 6 +-- cmake/Dependencies.cmake | 1 + dependencies/CMakeLists-qmdnsengine.txt.in | 2 +- dependencies/CMakeLists.txt | 8 ++-- include/api/JsonAPI.h | 5 ++ include/grabber/AudioGrabberWindows.h | 14 +++--- include/hyperion/ImageToLedsMap.h | 10 ++-- include/utils/global_defines.h | 2 +- libsrc/api/JsonAPI.cpp | 37 +++++++++------ libsrc/api/JsonCB.cpp | 2 +- libsrc/db/DBManager.cpp | 11 +++-- libsrc/effectengine/EffectFileHandler.cpp | 12 ++--- libsrc/forwarder/MessageForwarder.cpp | 10 ++-- libsrc/grabber/audio/AudioGrabber.cpp | 46 +++++++++++++------ libsrc/grabber/audio/AudioGrabberWindows.cpp | 8 +++- libsrc/grabber/audio/CMakeLists.txt | 2 +- libsrc/grabber/qt/QtGrabber.cpp | 2 +- libsrc/hyperion/ComponentRegister.cpp | 2 +- libsrc/hyperion/SettingsManager.cpp | 32 ++++++++----- .../leddevice/dev_net/LedDevicePhilipsHue.cpp | 2 +- libsrc/leddevice/dev_net/LedDeviceWled.cpp | 8 ++-- libsrc/utils/Logger.cpp | 2 +- .../utils/jsonschema/QJsonSchemaChecker.cpp | 4 +- src/hyperion-aml/CMakeLists.txt | 2 +- src/hyperion-dispmanx/CMakeLists.txt | 2 +- src/hyperion-framebuffer/CMakeLists.txt | 2 +- src/hyperion-osx/CMakeLists.txt | 2 +- src/hyperion-qt/CMakeLists.txt | 2 +- src/hyperion-remote/CMakeLists.txt | 2 +- src/hyperion-remote/hyperion-remote.cpp | 4 +- src/hyperion-v4l2/CMakeLists.txt | 2 +- src/hyperion-x11/CMakeLists.txt | 2 +- src/hyperion-xcb/CMakeLists.txt | 2 +- src/hyperiond/console.h | 1 + src/hyperiond/main.cpp | 4 +- 36 files changed, 169 insertions(+), 120 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cbb1ef3f..813cb750 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) message( STATUS "CMake Version: ${CMAKE_VERSION}" ) @@ -38,26 +38,24 @@ if ( CCACHE_FOUND ) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif(CCACHE_FOUND) -# enable C++14; MSVC doesn't have c++14 feature switch -if(NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - if(APPLE) - include(CheckCXXCompilerFlag) - CHECK_CXX_COMPILER_FLAG("Werror=unguarded-availability" REQUIRED_UNGUARDED_AVAILABILITY) - if(REQUIRED_UNGUARDED_AVAILABILITY) - list(APPEND CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "Werror=unguarded-availability") - endif() +# enable C++17 +if(APPLE) + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("Werror=unguarded-availability" REQUIRED_UNGUARDED_AVAILABILITY) + if(REQUIRED_UNGUARDED_AVAILABILITY) + list(APPEND CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "Werror=unguarded-availability") endif() - - if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-psabi") - endif() - - set(CMAKE_CXX_STANDARD 14) - set(CXX_STANDARD_REQUIRED ON) - set(CXX_EXTENSIONS OFF) endif() +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-psabi") +endif() + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + # Set build variables # Grabber SET ( DEFAULT_AMLOGIC OFF ) diff --git a/assets/webconfig/js/content_grabber.js b/assets/webconfig/js/content_grabber.js index 20da6030..6136673a 100755 --- a/assets/webconfig/js/content_grabber.js +++ b/assets/webconfig/js/content_grabber.js @@ -327,7 +327,7 @@ $(document).ready(function () { var saveOptions = conf_editor_screen.getValue(); var instCaptOptions = window.serverConfig.instCapture; - instCaptOptions.systemEnable = true; + instCaptOptions.systemEnable = saveOptions.framegrabber.enable; saveOptions.instCapture = instCaptOptions; requestWriteConfig(saveOptions); @@ -679,7 +679,7 @@ $(document).ready(function () { var saveOptions = conf_editor_video.getValue(); var instCaptOptions = window.serverConfig.instCapture; - instCaptOptions.v4lEnable = true; + instCaptOptions.v4lEnable = saveOptions.grabberV4L2.enable; saveOptions.instCapture = instCaptOptions; requestWriteConfig(saveOptions); @@ -805,7 +805,7 @@ $(document).ready(function () { const saveOptions = conf_editor_audio.getValue(); const instCaptOptions = window.serverConfig.instCapture; - instCaptOptions.audioEnable = true; + instCaptOptions.audioEnable = saveOptions.grabberAudio.enable; saveOptions.instCapture = instCaptOptions; requestWriteConfig(saveOptions); diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 08120831..40ada3e1 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -48,6 +48,7 @@ macro(DeployMacOS TARGET) foreach(PLUGIN "platforms" "sqldrivers" "imageformats") if(EXISTS ${PLUGIN_DIR}/${PLUGIN}) file(GLOB files "${PLUGIN_DIR}/${PLUGIN}/*") + list(FILTER files EXCLUDE REGEX ".*libqwebp\\.dylib$") foreach(file ${files}) file(GET_RUNTIME_DEPENDENCIES EXECUTABLES ${file} diff --git a/dependencies/CMakeLists-qmdnsengine.txt.in b/dependencies/CMakeLists-qmdnsengine.txt.in index 12e45328..b3f0812a 100644 --- a/dependencies/CMakeLists-qmdnsengine.txt.in +++ b/dependencies/CMakeLists-qmdnsengine.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) project(qmdnsengine) diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index c13e96cc..bc5044be 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -133,6 +133,9 @@ endif() if(ENABLE_PROTOBUF_SERVER) set(USE_SYSTEM_PROTO_LIBS ${DEFAULT_USE_SYSTEM_PROTO_LIBS} CACHE BOOL "use protobuf library from system") + + # defines for 3rd party sub-modules + set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "Build abseil-cpp with C++ version requirements propagated") if (USE_SYSTEM_PROTO_LIBS) find_package(Protobuf REQUIRED) @@ -161,9 +164,6 @@ if(ENABLE_PROTOBUF_SERVER) # define the protobuf library set(PROTOBUF_LIBRARIES protobuf::libprotobuf) - # defines for 3rd party sub-modules - set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "Build abseil-cpp with C++ version requirements propagated") - endif() # redefine at parent scope @@ -270,7 +270,7 @@ if(ENABLE_DEV_NETWORK) set(USE_SYSTEM_MBEDTLS_LIBS OFF) endif (NOT MBEDTLS_FOUND) else() - cmake_minimum_required(VERSION 3.2) + cmake_minimum_required(VERSION 3.5.1) set(CMAKE_POLICY_DEFAULT_CMP0071 NEW) diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h index 6346ce72..3880fc42 100644 --- a/include/api/JsonAPI.h +++ b/include/api/JsonAPI.h @@ -88,6 +88,11 @@ private slots: /// void handleInstanceStateChange(InstanceState state, quint8 instance, const QString &name = QString()); + /// + /// @brief Stream a new LED Colors update + /// + void streamLedColorsUpdate(); + signals: /// /// Signal emits with the reply message provided with handleMessage() diff --git a/include/grabber/AudioGrabberWindows.h b/include/grabber/AudioGrabberWindows.h index 747212c2..9c3945b6 100644 --- a/include/grabber/AudioGrabberWindows.h +++ b/include/grabber/AudioGrabberWindows.h @@ -39,8 +39,8 @@ class AudioGrabberWindows : public AudioGrabber HANDLE notificationEvent; std::atomic isRunning{ false }; -static BOOL CALLBACK DirectSoundEnumProcessor(LPGUID deviceIdGuid, LPCTSTR deviceDescStr, - LPCTSTR deviceModelStr, LPVOID context) +static BOOL CALLBACK DirectSoundEnumProcessor(LPGUID deviceIdGuid, LPCWSTR deviceDescStr, + LPCWSTR deviceModelStr, LPVOID context) { // Skip undefined audio devices if (deviceIdGuid == NULL) @@ -50,12 +50,15 @@ static BOOL CALLBACK DirectSoundEnumProcessor(LPGUID deviceIdGuid, LPCTSTR devic AudioGrabber::DeviceProperties device; + // Process Device Information + QString deviceName = QString::fromWCharArray(deviceDescStr); + // Process Device ID LPOLESTR deviceIdStr; HRESULT res = StringFromCLSID(*deviceIdGuid, &deviceIdStr); if (FAILED(res)) { - Error(Logger::getInstance("AUDIOGRABBER"), "Failed to get CLSID-string for %s with error: 0x%08x: %s", deviceDescStr, res, std::system_category().message(res).c_str()); + Error(Logger::getInstance("AUDIOGRABBER"), "Failed to get CLSID-string for %s with error: 0x%08x: %s", QSTRING_CSTR(deviceName), res, std::system_category().message(res).c_str()); return FALSE; } @@ -63,10 +66,7 @@ static BOOL CALLBACK DirectSoundEnumProcessor(LPGUID deviceIdGuid, LPCTSTR devic CoTaskMemFree(deviceIdStr); - // Process Device Information - QString deviceName = QString::fromLocal8Bit(deviceDescStr); - - Debug(Logger::getInstance("AUDIOGRABBER"), "Found Audio Device: %s", deviceDescStr); + Debug(Logger::getInstance("AUDIOGRABBER"), "Found Audio Device: %s", QSTRING_CSTR(deviceName)); device.id = deviceId; device.name = deviceName; diff --git a/include/hyperion/ImageToLedsMap.h b/include/hyperion/ImageToLedsMap.h index 17662f28..45e7bb5a 100644 --- a/include/hyperion/ImageToLedsMap.h +++ b/include/hyperion/ImageToLedsMap.h @@ -413,9 +413,13 @@ namespace hyperion } // Compute the average of each color channel - const uint8_t avgRed = uint8_t(std::min(std::lround(sqrt(static_cast(cummRed/pixelNum))), 255L)); - const uint8_t avgGreen = uint8_t(std::min(std::lround(sqrt(static_cast(cummGreen/pixelNum))), 255L)); - const uint8_t avgBlue = uint8_t(std::min(std::lround(sqrt(static_cast(cummBlue/pixelNum))), 255L)); + + #ifdef WIN32 + #undef min + #endif + const uint8_t avgRed = static_cast(std::min(std::lround(std::sqrt(static_cast(cummRed / pixelNum))), 255L)); + const uint8_t avgGreen = static_cast(std::min(std::lround(sqrt(static_cast(cummGreen / pixelNum))), 255L)); + const uint8_t avgBlue = static_cast(std::min(std::lround(sqrt(static_cast(cummBlue / pixelNum))), 255L)); // Return the computed color return {avgRed, avgGreen, avgBlue}; diff --git a/include/utils/global_defines.h b/include/utils/global_defines.h index e5a6808e..e948fb97 100644 --- a/include/utils/global_defines.h +++ b/include/utils/global_defines.h @@ -1,6 +1,6 @@ #pragma once -#define QSTRING_CSTR(str) str.toLocal8Bit().constData() +#define QSTRING_CSTR(str) str.toUtf8().constData() typedef QList< int > QIntList; diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 212dc14b..75863ca2 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -140,6 +140,8 @@ void JsonAPI::initialize() connect(this, &JsonAPI::toggleSuspendAll, _instanceManager, &HyperionIManager::triggerToggleSuspend); connect(this, &JsonAPI::idleAll, _instanceManager, &HyperionIManager::triggerIdle); connect(this, &JsonAPI::toggleIdleAll, _instanceManager, &HyperionIManager::triggerToggleIdle); + + connect(_ledStreamTimer, &QTimer::timeout, this, &JsonAPI::streamLedColorsUpdate, Qt::UniqueConnection); } bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced) @@ -404,7 +406,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString activePriorities.removeAll(PriorityMuxer::LOWEST_PRIORITY); int currentPriority = _hyperion->getCurrentPriority(); - for(int priority : qAsConst(activePriorities)) + for(int priority : std::as_const(activePriorities)) { const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(priority); @@ -1139,6 +1141,11 @@ void JsonAPI::handleComponentStateCommand(const QJsonObject &message, const QStr sendSuccessReply(command, tan); } +void JsonAPI::streamLedColorsUpdate() +{ + emit streamLedcolorsUpdate(_currentLedValues); +} + void JsonAPI::handleLedColorsCommand(const QJsonObject &message, const QString &command, int tan) { // create result @@ -1154,21 +1161,21 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject &message, const QString & _streaming_leds_reply["tan"] = tan; connect(_hyperion, &Hyperion::rawLedColors, this, [=](const std::vector &ledValues) { - _currentLedValues = ledValues; - // necessary because Qt::UniqueConnection for lambdas does not work until 5.9 - // see: https://bugreports.qt.io/browse/QTBUG-52438 - if (!_ledStreamConnection) - _ledStreamConnection = connect(_ledStreamTimer, &QTimer::timeout, this, [=]() { - emit streamLedcolorsUpdate(_currentLedValues); - }, - Qt::UniqueConnection); + if (ledValues != _currentLedValues) + { + _currentLedValues = ledValues; + if (!_ledStreamTimer->isActive() || _ledStreamTimer->interval() != streaming_interval) + { + _ledStreamTimer->start(streaming_interval); + } + } + else + { + _ledStreamTimer->stop(); + } + }); - // start the timer - if (!_ledStreamTimer->isActive() || _ledStreamTimer->interval() != streaming_interval) - _ledStreamTimer->start(streaming_interval); - }, - Qt::UniqueConnection); // push once _hyperion->update(); } @@ -1387,7 +1394,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString & if (API::getPendingTokenRequests(vec)) { QJsonArray arr; - for (const auto &entry : qAsConst(vec)) + for (const auto &entry : std::as_const(vec)) { QJsonObject obj; obj["comment"] = entry.comment; diff --git a/libsrc/api/JsonCB.cpp b/libsrc/api/JsonCB.cpp index 965abf37..e3c4b32a 100644 --- a/libsrc/api/JsonCB.cpp +++ b/libsrc/api/JsonCB.cpp @@ -199,7 +199,7 @@ void JsonCB::handlePriorityUpdate(int currentPriority, const PriorityMuxer::Inpu activePriorities.removeAll(PriorityMuxer::LOWEST_PRIORITY); - for (int priority : qAsConst(activePriorities)) { + for (int priority : std::as_const(activePriorities)) { const Hyperion::InputInfo& priorityInfo = activeInputs[priority]; diff --git a/libsrc/db/DBManager.cpp b/libsrc/db/DBManager.cpp index a8711c15..f4494967 100644 --- a/libsrc/db/DBManager.cpp +++ b/libsrc/db/DBManager.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -425,15 +426,15 @@ void DBManager::doAddBindValue(QSqlQuery& query, const QVariantList& variants) c auto t = variant.userType(); switch(t) { - case QVariant::UInt: - case QVariant::Int: - case QVariant::Bool: + case QMetaType::UInt: + case QMetaType::Int: + case QMetaType::Bool: query.addBindValue(variant.toInt()); break; - case QVariant::Double: + case QMetaType::Double: query.addBindValue(variant.toFloat()); break; - case QVariant::ByteArray: + case QMetaType::QByteArray: query.addBindValue(variant.toByteArray()); break; default: diff --git a/libsrc/effectengine/EffectFileHandler.cpp b/libsrc/effectengine/EffectFileHandler.cpp index e1389e83..3d1c77eb 100644 --- a/libsrc/effectengine/EffectFileHandler.cpp +++ b/libsrc/effectengine/EffectFileHandler.cpp @@ -224,7 +224,7 @@ void EffectFileHandler::updateEffects() } QMap availableEffects; - for (const QString& path : qAsConst(efxPathList)) + for (const QString& path : std::as_const(efxPathList)) { QDir directory(path); if (!directory.exists()) @@ -241,8 +241,8 @@ void EffectFileHandler::updateEffects() else { int efxCount = 0; - QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase); - for (const QString& filename : qAsConst(filenames)) + const QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase); + for (const QString& filename : filenames) { EffectDefinition def; if (loadEffectDefinition(path, filename, def)) @@ -268,8 +268,8 @@ void EffectFileHandler::updateEffects() QString schemaPath = path + "schema" + '/'; directory.setPath(schemaPath); - QStringList schemaFileNames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase); - for (const QString& schemaFileName : qAsConst(schemaFileNames)) + const QStringList schemaFileNames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase); + for (const QString& schemaFileName : schemaFileNames) { EffectSchema pyEffect; if (loadEffectSchema(path, directory.filePath(schemaFileName), pyEffect)) @@ -282,7 +282,7 @@ void EffectFileHandler::updateEffects() } } - for (const auto& item : qAsConst(availableEffects)) + for (const auto& item : std::as_const(availableEffects)) { _availableEffects.push_back(item); } diff --git a/libsrc/forwarder/MessageForwarder.cpp b/libsrc/forwarder/MessageForwarder.cpp index b795cbe8..3720ad54 100644 --- a/libsrc/forwarder/MessageForwarder.cpp +++ b/libsrc/forwarder/MessageForwarder.cpp @@ -269,7 +269,7 @@ int MessageForwarder::startJsonTargets(const QJsonObject& config) if (!_jsonTargets.isEmpty()) { - for (const auto& targetHost : qAsConst(_jsonTargets)) + for (const auto& targetHost : std::as_const(_jsonTargets)) { Info(_log, "Forwarding now to JSON-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port); } @@ -286,7 +286,7 @@ void MessageForwarder::stopJsonTargets() if (!_jsonTargets.isEmpty()) { disconnect(_hyperion, &Hyperion::forwardJsonMessage, nullptr, nullptr); - for (const auto& targetHost : qAsConst(_jsonTargets)) + for (const auto& targetHost : std::as_const(_jsonTargets)) { Info(_log, "Stopped forwarding to JSON-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port); } @@ -373,7 +373,7 @@ int MessageForwarder::startFlatbufferTargets(const QJsonObject& config) if (!_flatbufferTargets.isEmpty()) { - for (const auto& targetHost : qAsConst(_flatbufferTargets)) + for (const auto& targetHost : std::as_const(_flatbufferTargets)) { Info(_log, "Forwarding now to Flatbuffer-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port); } @@ -399,7 +399,7 @@ void MessageForwarder::stopFlatbufferTargets() _messageForwarderFlatBufHelper = nullptr; } - for (const auto& targetHost : qAsConst(_flatbufferTargets)) + for (const auto& targetHost : std::as_const(_flatbufferTargets)) { Info(_log, "Stopped forwarding to Flatbuffer-target host: %s port: %u", QSTRING_CSTR(targetHost.host.toString()), targetHost.port); } @@ -412,7 +412,7 @@ void MessageForwarder::forwardJsonMessage(const QJsonObject& message) if (_forwarder_enabled) { QTcpSocket client; - for (const auto& targetHost : qAsConst(_jsonTargets)) + for (const auto& targetHost : std::as_const(_jsonTargets)) { client.connectToHost(targetHost.host, targetHost.port); if (client.waitForConnected(CONNECT_TIMEOUT.count())) diff --git a/libsrc/grabber/audio/AudioGrabber.cpp b/libsrc/grabber/audio/AudioGrabber.cpp index 1e625a1e..4f4eccbd 100644 --- a/libsrc/grabber/audio/AudioGrabber.cpp +++ b/libsrc/grabber/audio/AudioGrabber.cpp @@ -9,6 +9,15 @@ // Constants namespace { const uint16_t RESOLUTION = 255; + + //Constants vuMeter + const QJsonArray DEFAULT_HOTCOLOR { 255,0,0 }; + const QJsonArray DEFAULT_WARNCOLOR { 255,255,0 }; + const QJsonArray DEFAULT_SAFECOLOR { 0,255,0 }; + const int DEFAULT_WARNVALUE { 80 }; + const int DEFAULT_SAFEVALUE { 45 }; + const int DEFAULT_MULTIPLIER { 0 }; + const int DEFAULT_TOLERANCE { 20 }; } #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) @@ -28,12 +37,12 @@ AudioGrabber::AudioGrabber() , _deviceProperties() , _device("none") , _hotColor(QColorConstants::Red) - , _warnValue(80) + , _warnValue(DEFAULT_WARNVALUE) , _warnColor(QColorConstants::Yellow) - , _safeValue(45) + , _safeValue(DEFAULT_SAFEVALUE) , _safeColor(QColorConstants::Green) - , _multiplier(0) - , _tolerance(20) + , _multiplier(DEFAULT_MULTIPLIER) + , _tolerance(DEFAULT_TOLERANCE) , _dynamicMultiplier(INT16_MAX) , _started(false) { @@ -61,18 +70,27 @@ void AudioGrabber::setDevice(const QString& device) void AudioGrabber::setConfiguration(const QJsonObject& config) { - QJsonArray hotColorArray = config["hotColor"].toArray(QJsonArray::fromVariantList(QList({ QVariant(255), QVariant(0), QVariant(0) }))); - QJsonArray warnColorArray = config["warnColor"].toArray(QJsonArray::fromVariantList(QList({ QVariant(255), QVariant(255), QVariant(0) }))); - QJsonArray safeColorArray = config["safeColor"].toArray(QJsonArray::fromVariantList(QList({ QVariant(0), QVariant(255), QVariant(0) }))); + QString audioEffect = config["audioEffect"].toString(); + QJsonObject audioEffectConfig = config[audioEffect].toObject(); - _hotColor = QColor(hotColorArray.at(0).toInt(), hotColorArray.at(1).toInt(), hotColorArray.at(2).toInt()); - _warnColor = QColor(warnColorArray.at(0).toInt(), warnColorArray.at(1).toInt(), warnColorArray.at(2).toInt()); - _safeColor = QColor(safeColorArray.at(0).toInt(), safeColorArray.at(1).toInt(), safeColorArray.at(2).toInt()); + if (audioEffect == "vuMeter") + { + QJsonArray hotColorArray = audioEffectConfig.value("hotColor").toArray(DEFAULT_HOTCOLOR); + QJsonArray warnColorArray = audioEffectConfig.value("warnColor").toArray(DEFAULT_WARNCOLOR); + QJsonArray safeColorArray = audioEffectConfig.value("safeColor").toArray(DEFAULT_SAFECOLOR); - _warnValue = config["warnValue"].toInt(80); - _safeValue = config["safeValue"].toInt(45); - _multiplier = config["multiplier"].toDouble(0); - _tolerance = config["tolerance"].toInt(20); + _hotColor = QColor(hotColorArray.at(0).toInt(), hotColorArray.at(1).toInt(), hotColorArray.at(2).toInt()); + _warnColor = QColor(warnColorArray.at(0).toInt(), warnColorArray.at(1).toInt(), warnColorArray.at(2).toInt()); + _safeColor = QColor(safeColorArray.at(0).toInt(), safeColorArray.at(1).toInt(), safeColorArray.at(2).toInt()); + _warnValue = audioEffectConfig["warnValue"].toInt(DEFAULT_WARNVALUE); + _safeValue = audioEffectConfig["safeValue"].toInt(DEFAULT_SAFEVALUE); + _multiplier = audioEffectConfig["multiplier"].toDouble(DEFAULT_MULTIPLIER); + _tolerance = audioEffectConfig["tolerance"].toInt(DEFAULT_MULTIPLIER); + } + else + { + Error(_log, "Unknow Audio-Effect: \"%s\" configured", QSTRING_CSTR(audioEffect)); + } } void AudioGrabber::resetMultiplier() diff --git a/libsrc/grabber/audio/AudioGrabberWindows.cpp b/libsrc/grabber/audio/AudioGrabberWindows.cpp index 07837bd1..8a2228c3 100644 --- a/libsrc/grabber/audio/AudioGrabberWindows.cpp +++ b/libsrc/grabber/audio/AudioGrabberWindows.cpp @@ -1,4 +1,7 @@ #include + +#include + #include #include #include @@ -61,7 +64,10 @@ bool AudioGrabberWindows::configureCaptureInterface() // wFormatTag, nChannels, nSamplesPerSec, mAvgBytesPerSec, // nBlockAlign, wBitsPerSample, cbSize - notificationSize = max(1024, audioFormat.nAvgBytesPerSec / 8); + #ifdef WIN32 + #undef max + #endif + notificationSize = std::max(static_cast(1024), static_cast(audioFormat.nAvgBytesPerSec / 8)); notificationSize -= notificationSize % audioFormat.nBlockAlign; bufferCaptureSize = notificationSize * AUDIO_NOTIFICATION_COUNT; diff --git a/libsrc/grabber/audio/CMakeLists.txt b/libsrc/grabber/audio/CMakeLists.txt index 187fa9d0..714c5883 100644 --- a/libsrc/grabber/audio/CMakeLists.txt +++ b/libsrc/grabber/audio/CMakeLists.txt @@ -2,8 +2,8 @@ SET( CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber ) SET( CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/audio ) - if (WIN32) + add_definitions(-DUNICODE -D_UNICODE) FILE ( GLOB AUDIO_GRABBER_SOURCES "${CURRENT_HEADER_DIR}/Audio*Windows.h" "${CURRENT_HEADER_DIR}/AudioGrabber.h" "${CURRENT_HEADER_DIR}/AudioWrapper.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*Windows.cpp" "${CURRENT_SOURCE_DIR}/AudioGrabber.cpp" "${CURRENT_SOURCE_DIR}/AudioWrapper.cpp") elseif(${CMAKE_SYSTEM} MATCHES "Linux") FILE ( GLOB AUDIO_GRABBER_SOURCES "${CURRENT_HEADER_DIR}/Audio*Linux.h" "${CURRENT_HEADER_DIR}/AudioGrabber.h" "${CURRENT_HEADER_DIR}/AudioWrapper.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*Linux.cpp" "${CURRENT_SOURCE_DIR}/AudioGrabber.cpp" "${CURRENT_SOURCE_DIR}/AudioWrapper.cpp") diff --git a/libsrc/grabber/qt/QtGrabber.cpp b/libsrc/grabber/qt/QtGrabber.cpp index b3f35182..cb6e0c5c 100644 --- a/libsrc/grabber/qt/QtGrabber.cpp +++ b/libsrc/grabber/qt/QtGrabber.cpp @@ -103,7 +103,7 @@ bool QtGrabber::setupDisplay() Info(_log, "Available Displays:"); int index = 0; - for (auto* screen : qAsConst(screens)) + for (auto* screen : std::as_const(screens)) { const QRect geo = screen->geometry(); Info(_log, "Display %d: Name: %s Resolution: [%dx%d], Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", index, QSTRING_CSTR(screen->name()), geo.width(), geo.height(), geo.x(), geo.y(), geo.x() + geo.width(), geo.y() + geo.height(), screen->depth()); diff --git a/libsrc/hyperion/ComponentRegister.cpp b/libsrc/hyperion/ComponentRegister.cpp index fd2f261d..9a040caf 100644 --- a/libsrc/hyperion/ComponentRegister.cpp +++ b/libsrc/hyperion/ComponentRegister.cpp @@ -61,7 +61,7 @@ ComponentRegister::ComponentRegister(Hyperion* hyperion) vect << COMP_FORWARDER; #endif - for(auto e : qAsConst(vect)) + for(auto e : std::as_const(vect)) { _componentStates.emplace(e, (e == COMP_ALL)); } diff --git a/libsrc/hyperion/SettingsManager.cpp b/libsrc/hyperion/SettingsManager.cpp index b9d78edc..e9fa5805 100644 --- a/libsrc/hyperion/SettingsManager.cpp +++ b/libsrc/hyperion/SettingsManager.cpp @@ -58,9 +58,9 @@ SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonly } // transform json to string lists - QStringList keyList = defaultConfig.keys(); + const QStringList keyList = defaultConfig.keys(); QStringList defValueList; - for (const auto& key : qAsConst(keyList)) + for (const auto& key : keyList) { if (defaultConfig[key].isObject()) { @@ -73,7 +73,7 @@ SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonly } // fill database with default data if required - for (const auto& key : qAsConst(keyList)) + for (const auto& key : keyList) { QString val = defValueList.takeFirst(); // prevent overwrite @@ -86,7 +86,7 @@ SettingsManager::SettingsManager(quint8 instance, QObject* parent, bool readonly // need to validate all data in database construct the entire data object // TODO refactor schemaChecker to accept QJsonArray in validate(); QJsonDocument container? To validate them per entry... QJsonObject dbConfig; - for (const auto& key : qAsConst(keyList)) + for (const auto& key : keyList) { QJsonDocument doc = _sTable->getSettingsRecord(key); if (doc.isArray()) @@ -242,9 +242,9 @@ bool SettingsManager::saveSettings(QJsonObject config, bool correct) _qconfig = config; // extract keys and data - QStringList keyList = config.keys(); + const QStringList keyList = config.keys(); QStringList newValueList; - for (const auto& key : qAsConst(keyList)) + for (const auto& key : keyList) { if (config[key].isObject()) { @@ -258,7 +258,7 @@ bool SettingsManager::saveSettings(QJsonObject config, bool correct) bool rc = true; // compare database data with new data to emit/save changes accordingly - for (const auto& key : qAsConst(keyList)) + for (const auto& key : keyList) { QString data = newValueList.takeFirst(); if (_sTable->getSettingsRecordString(key) != data) @@ -269,7 +269,15 @@ bool SettingsManager::saveSettings(QJsonObject config, bool correct) } else { - emit settingsChanged(settings::stringToType(key), QJsonDocument::fromJson(data.toLocal8Bit())); + QJsonParseError error; + QJsonDocument jsonDocument = QJsonDocument::fromJson(data.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) { + Error(_log, "Error parsing JSON: %s", QSTRING_CSTR(error.errorString())); + rc = false; + } + else { + emit settingsChanged(settings::stringToType(key), jsonDocument); + } } } } @@ -618,10 +626,10 @@ bool SettingsManager::handleConfigUpgrade(QJsonObject& config) QJsonArray json; if (newForwarderConfig.contains("json")) { - QJsonArray oldJson = newForwarderConfig["json"].toArray(); + const QJsonArray oldJson = newForwarderConfig["json"].toArray(); QJsonObject newJsonConfig; - for (const QJsonValue& value : qAsConst(oldJson)) + for (const QJsonValue& value : oldJson) { if (value.isString()) { @@ -661,10 +669,10 @@ bool SettingsManager::handleConfigUpgrade(QJsonObject& config) QJsonArray flatbuffer; if (newForwarderConfig.contains("flat")) { - QJsonArray oldFlatbuffer = newForwarderConfig["flat"].toArray(); + const QJsonArray oldFlatbuffer = newForwarderConfig["flat"].toArray(); QJsonObject newFlattbufferConfig; - for (const QJsonValue& value : qAsConst(oldFlatbuffer)) + for (const QJsonValue& value : oldFlatbuffer) { if (value.isString()) { diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp index 8f73ae5f..ba008ab0 100644 --- a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp @@ -1126,7 +1126,7 @@ bool LedDevicePhilipsHue::setLights() if( !lArray.empty() ) { - for (const QJsonValue &id : qAsConst(lArray)) + for (const QJsonValue &id : std::as_const(lArray)) { int lightId = id.toString().toInt(); if( lightId > 0 ) diff --git a/libsrc/leddevice/dev_net/LedDeviceWled.cpp b/libsrc/leddevice/dev_net/LedDeviceWled.cpp index 3ada5091..e23a8c9b 100644 --- a/libsrc/leddevice/dev_net/LedDeviceWled.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceWled.cpp @@ -352,12 +352,12 @@ bool LedDeviceWled::powerOn() } else { - QJsonArray propertiesSegments = _originalStateProperties[STATE_SEG].toArray(); + const QJsonArray propertiesSegments = _originalStateProperties[STATE_SEG].toArray(); bool isStreamSegmentIdFound { false }; QJsonArray segments; - for (const auto& segmentItem : qAsConst(propertiesSegments)) + for (const auto& segmentItem : propertiesSegments) { QJsonObject segmentObj = segmentItem.toObject(); @@ -505,9 +505,9 @@ bool LedDeviceWled::restoreState() if (_isStreamToSegment) { - QJsonArray propertiesSegments = _originalStateProperties[STATE_SEG].toArray(); + const QJsonArray propertiesSegments = _originalStateProperties[STATE_SEG].toArray(); QJsonArray segments; - for (const auto& segmentItem : qAsConst(propertiesSegments)) + for (const auto& segmentItem : propertiesSegments) { QJsonObject segmentObj = segmentItem.toObject(); diff --git a/libsrc/utils/Logger.cpp b/libsrc/utils/Logger.cpp index 86b4e903..b34981c9 100644 --- a/libsrc/utils/Logger.cpp +++ b/libsrc/utils/Logger.cpp @@ -65,7 +65,7 @@ void Logger::deleteInstance(const QString & name, const QString & subName) if (name.isEmpty()) { - for (auto *logger : qAsConst(LoggerMap)) { + for (auto *logger : std::as_const(LoggerMap)) { delete logger; } diff --git a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp index 685ab4c7..8cd0be75 100644 --- a/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp +++ b/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp @@ -59,13 +59,13 @@ QPair QJsonSchemaChecker::validate(const QJsonObject& value, bool ig QJsonObject QJsonSchemaChecker::getAutoCorrectedConfig(const QJsonObject& value, bool ignoreRequired) { _ignoreRequired = ignoreRequired; - QStringList sequence = QStringList() << "remove" << "modify" << "create"; + const QStringList sequence = QStringList() << "remove" << "modify" << "create"; _error = false; _schemaError = false; _messages.clear(); _autoCorrected = value; - for (const QString& correct : qAsConst(sequence)) + for (const QString& correct : sequence) { _correct = correct; _currentPath.clear(); diff --git a/src/hyperion-aml/CMakeLists.txt b/src/hyperion-aml/CMakeLists.txt index 37bed4af..cdea66fa 100644 --- a/src/hyperion-aml/CMakeLists.txt +++ b/src/hyperion-aml/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-aml) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperion-dispmanx/CMakeLists.txt b/src/hyperion-dispmanx/CMakeLists.txt index 17720483..07862bbd 100644 --- a/src/hyperion-dispmanx/CMakeLists.txt +++ b/src/hyperion-dispmanx/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-dispmanx) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperion-framebuffer/CMakeLists.txt b/src/hyperion-framebuffer/CMakeLists.txt index 5667203a..14ea2ce9 100644 --- a/src/hyperion-framebuffer/CMakeLists.txt +++ b/src/hyperion-framebuffer/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-framebuffer) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperion-osx/CMakeLists.txt b/src/hyperion-osx/CMakeLists.txt index 12f17a9b..efb15cd1 100644 --- a/src/hyperion-osx/CMakeLists.txt +++ b/src/hyperion-osx/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-osx) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Widgets REQUIRED) diff --git a/src/hyperion-qt/CMakeLists.txt b/src/hyperion-qt/CMakeLists.txt index 6cc4b386..f35b8534 100644 --- a/src/hyperion-qt/CMakeLists.txt +++ b/src/hyperion-qt/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-qt) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Widgets REQUIRED) diff --git a/src/hyperion-remote/CMakeLists.txt b/src/hyperion-remote/CMakeLists.txt index a7ce5cfe..a2805cad 100644 --- a/src/hyperion-remote/CMakeLists.txt +++ b/src/hyperion-remote/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-remote) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperion-remote/hyperion-remote.cpp b/src/hyperion-remote/hyperion-remote.cpp index f47fbb14..e7fdc453 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -57,9 +57,9 @@ void showHelp(Option & option){ int getInstaneIdbyName(const QJsonObject & reply, const QString & name){ if(reply.contains("instance")){ - QJsonArray list = reply.value("instance").toArray(); + const QJsonArray list = reply.value("instance").toArray(); - for ( const auto &entry : qAsConst(list) ) { + for ( const auto &entry : list ) { const QJsonObject obj = entry.toObject(); if(obj["friendly_name"] == name && obj["running"].toBool()) { diff --git a/src/hyperion-v4l2/CMakeLists.txt b/src/hyperion-v4l2/CMakeLists.txt index 9c8783dd..1404d2ad 100644 --- a/src/hyperion-v4l2/CMakeLists.txt +++ b/src/hyperion-v4l2/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-v4l2) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperion-x11/CMakeLists.txt b/src/hyperion-x11/CMakeLists.txt index a7459ce6..88095ae3 100644 --- a/src/hyperion-x11/CMakeLists.txt +++ b/src/hyperion-x11/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-x11) find_package(X11 REQUIRED) diff --git a/src/hyperion-xcb/CMakeLists.txt b/src/hyperion-xcb/CMakeLists.txt index cd374d5a..f378aef2 100644 --- a/src/hyperion-xcb/CMakeLists.txt +++ b/src/hyperion-xcb/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.5.0) project(hyperion-xcb) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) diff --git a/src/hyperiond/console.h b/src/hyperiond/console.h index 98469960..e61156ae 100644 --- a/src/hyperiond/console.h +++ b/src/hyperiond/console.h @@ -15,4 +15,5 @@ void CreateConsole() freopen_s(&fDummy, "CONOUT$", "w", stderr); freopen_s(&fDummy, "CONIN$", "r", stdin); SetConsoleTitle(TEXT("Hyperion")); + SetConsoleOutputCP(CP_UTF8); } diff --git a/src/hyperiond/main.cpp b/src/hyperiond/main.cpp index 4719cd22..fa9352b9 100644 --- a/src/hyperiond/main.cpp +++ b/src/hyperiond/main.cpp @@ -268,9 +268,9 @@ int main(int argc, char** argv) if (directory.exists() && destDir.exists()) { std::cout << "Extract to folder: " << destDir.absolutePath().toStdString() << std::endl; - QStringList filenames = directory.entryList(QStringList() << "*", QDir::Files, QDir::Name | QDir::IgnoreCase); + const QStringList filenames = directory.entryList(QStringList() << "*", QDir::Files, QDir::Name | QDir::IgnoreCase); QString destFileName; - for (const QString & filename : qAsConst(filenames)) + for (const QString & filename : filenames) { destFileName = destDir.dirName()+"/"+filename; if (QFile::exists(destFileName)) From f49e1a2c0be690d45d0e169e2267318d166f80ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:03:57 +0200 Subject: [PATCH 27/98] Bump actions/checkout from 3 to 4 (#1646) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/apt.yml | 6 +++--- .github/workflows/codeql.yml | 2 +- .github/workflows/nightly.yml | 10 +++++----- .github/workflows/pull-request.yml | 6 +++--- .github/workflows/push-master.yml | 8 ++++---- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/apt.yml b/.github/workflows/apt.yml index 66b90cb0..39f440b2 100644 --- a/.github/workflows/apt.yml +++ b/.github/workflows/apt.yml @@ -36,7 +36,7 @@ jobs: name: Setup APT build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set APT matrix id: apt-ppa run: | @@ -54,7 +54,7 @@ jobs: matrix: ${{ fromJson(needs.setup.outputs.apt-matrix) }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.head_sha || github.event.client_payload.head_sha }} submodules: true @@ -107,7 +107,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.head_sha || github.event.client_payload.head_sha }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1565d699..d39190cc 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7151ce2f..70d63aab 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -13,7 +13,7 @@ jobs: if: github.repository_owner == 'hyperion-project' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: persist-credentials: false fetch-depth: 0 @@ -48,7 +48,7 @@ jobs: if: github.repository_owner == 'hyperion-project' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Check if commit has changed id: build-necessary run: | @@ -66,7 +66,7 @@ jobs: if: ${{ needs.check.outputs.build-nightly == 'true' }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set nightly matrix id: nightly-ppa run: | @@ -84,7 +84,7 @@ jobs: matrix: ${{ fromJson(needs.setup.outputs.nightly-matrix) }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -135,7 +135,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Import GPG key uses: crazy-max/ghaction-import-gpg@v5.3.0 diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 47769bfb..eee7c150 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive @@ -76,7 +76,7 @@ jobs: runs-on: macos-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive @@ -125,7 +125,7 @@ jobs: QT_VERSION: 5.15.2 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index 8f1b44c3..b0ee7a1d 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -33,7 +33,7 @@ jobs: platform: amlogic steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -62,7 +62,7 @@ jobs: name: macOS runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -97,7 +97,7 @@ jobs: QT_VERSION: 5.15.2 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive @@ -162,7 +162,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Generate environment variables - name: Generate environment variables from .version and tag From 41bffecc0b5bc2b651f3d48516536937ad58f331 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:04:26 +0200 Subject: [PATCH 28/98] Bump peter-evans/repository-dispatch from 2.1.1 to 2.1.2 (#1645) Bumps [peter-evans/repository-dispatch](https://github.com/peter-evans/repository-dispatch) from 2.1.1 to 2.1.2. - [Release notes](https://github.com/peter-evans/repository-dispatch/releases) - [Commits](https://github.com/peter-evans/repository-dispatch/compare/v2.1.1...v2.1.2) --- updated-dependencies: - dependency-name: peter-evans/repository-dispatch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d4c8461a..104c2c79 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: steps: # Dispatch event to build new HyperBian image - name: Dispatch HyperBian build - uses: peter-evans/repository-dispatch@v2.1.1 + uses: peter-evans/repository-dispatch@v2.1.2 if: ${{ github.repository_owner == 'hyperion-project'}} with: repository: hyperion-project/HyperBian From 33722c9a09430f0b0f569d56b0a176ee9e4664ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:06:39 +0200 Subject: [PATCH 29/98] Bump crazy-max/ghaction-import-gpg from 5.3.0 to 6.0.0 (#1644) Bumps [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) from 5.3.0 to 6.0.0. - [Release notes](https://github.com/crazy-max/ghaction-import-gpg/releases) - [Commits](https://github.com/crazy-max/ghaction-import-gpg/compare/v5.3.0...v6.0.0) --- updated-dependencies: - dependency-name: crazy-max/ghaction-import-gpg dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/apt.yml | 2 +- .github/workflows/nightly.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/apt.yml b/.github/workflows/apt.yml index 39f440b2..99bce214 100644 --- a/.github/workflows/apt.yml +++ b/.github/workflows/apt.yml @@ -112,7 +112,7 @@ jobs: ref: ${{ github.event.inputs.head_sha || github.event.client_payload.head_sha }} - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v5.3.0 + uses: crazy-max/ghaction-import-gpg@v6.0.0 with: gpg_private_key: ${{ secrets.APT_GPG }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 70d63aab..2046ba07 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -138,7 +138,7 @@ jobs: uses: actions/checkout@v4 - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v5.3.0 + uses: crazy-max/ghaction-import-gpg@v6.0.0 with: gpg_private_key: ${{ secrets.APT_GPG }} From cd22d4454d99ae7bc84cabb89affcd32766c079c Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Tue, 3 Oct 2023 20:33:11 +0200 Subject: [PATCH 30/98] Philips Hue APIv2 support (#1637) * Support Philips Hue APIv2 and refactoring * Fix MDNSBrower - if timeout during host resolvment occurs * Hue API v2 - Migrate database * Fix macOS build * Handle network timeout before any other error * Address CodeQL findings * Clean-up and Fixes * Only getProperties, if username is available * Option to layout by entertainment area center * Fix Wizard --------- Co-authored-by: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com> --- assets/webconfig/i18n/en.json | 7 +- assets/webconfig/js/content_leds.js | 16 +- assets/webconfig/js/wizard.js | 720 ++++-- libsrc/hyperion/SettingsManager.cpp | 55 + .../leddevice/dev_net/LedDevicePhilipsHue.cpp | 2018 ++++++++++++----- .../leddevice/dev_net/LedDevicePhilipsHue.h | 203 +- libsrc/leddevice/dev_net/ProviderRestApi.cpp | 203 +- libsrc/leddevice/dev_net/ProviderRestApi.h | 74 +- libsrc/leddevice/dev_net/ProviderUdpSSL.cpp | 10 + libsrc/leddevice/dev_net/ProviderUdpSSL.h | 10 + .../leddevice/schemas/schema-philipshue.json | 108 +- libsrc/mdns/MdnsBrowser.cpp | 1 + resources/ssl/philips_hue_ca.pem | 14 + 13 files changed, 2541 insertions(+), 898 deletions(-) create mode 100644 resources/ssl/philips_hue_ca.pem diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 263c8bd6..d8518fd2 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -86,6 +86,8 @@ "conf_leds_layout_cl_bottomright": "Bottom Right (Corner)", "conf_leds_layout_cl_cornergap": "Corner Gap", "conf_leds_layout_cl_edgegap": "Edge Gap", + "conf_leds_layout_cl_entertainment": "Entertainment Area", + "conf_leds_layout_cl_entertainment_center": "Entertainment Area Center", "conf_leds_layout_cl_gaglength": "Gap length", "conf_leds_layout_cl_gappos": "gap position", "conf_leds_layout_cl_hleddepth": "Horizontal LED depth", @@ -618,7 +620,7 @@ "edt_dev_spec_gpioBcm_title": "GPIO Pin", "edt_dev_spec_gpioMap_title": "GPIO mapping", "edt_dev_spec_gpioNumber_title": "GPIO number", - "edt_dev_spec_groupId_title": "Group ID", + "edt_dev_spec_groupId_title": "Group", "edt_dev_spec_header_title": "Specific Settings", "edt_dev_spec_interpolation_title": "Interpolation", "edt_dev_spec_intervall_title": "Interval", @@ -683,6 +685,7 @@ "edt_dev_spec_transistionTime_title": "Transition time", "edt_dev_spec_uid_title": "UID", "edt_dev_spec_universe_title": "Universe", + "edt_dev_spec_useAPIv2_title": "Use API v2", "edt_dev_spec_useEntertainmentAPI_title": "Use Hue Entertainment API", "edt_dev_spec_useOrbSmoothing_title": "Use orb smoothing", "edt_dev_spec_useRgbwProtocol_title": "Use RGBW protocol", @@ -1087,7 +1090,7 @@ "wiz_cololight_noprops": "Not able to get device properties - Define Hardware LED count manually", "wiz_cololight_title": "Cololight Wizard", "wiz_guideyou": "The $1 will guide you through the settings. Just press the button!", - "wiz_hue_blinkblue": "Let ID $1 light up blue", + "wiz_hue_blinkblue": "Let it light up", "wiz_hue_clientkey": "Clientkey", "wiz_hue_create_user": "Create new User", "wiz_hue_desc1": "1. Hyperion searches automatically for a Hue-Bridge, in case it cannot find one you need to provide the hostname or IP-address and push the reload button.
2. Provide a user ID, if you do not have one create a new one.", diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js index 1aec94ab..2b0304c5 100755 --- a/assets/webconfig/js/content_leds.js +++ b/assets/webconfig/js/content_leds.js @@ -1021,11 +1021,6 @@ $(document).ready(function () { var generalOptions = window.serverSchema.properties.device; var ledType = $(this).val(); - - // philipshueentertainment backward fix - if (ledType == "philipshueentertainment") - ledType = "philipshue"; - var specificOptions = window.serverSchema.properties.alldevices[ledType]; conf_editor = createJsonEditor('editor_container_leddevice', { @@ -1060,13 +1055,10 @@ $(document).ready(function () { $('#btn_led_device_wiz').off(); if (ledType == "philipshue") { - $('#root_specificOptions_useEntertainmentAPI').on("change", function () { - var ledWizardType = (this.checked) ? "philipshueentertainment" : ledType; - var data = { type: ledWizardType }; - var hue_title = (this.checked) ? 'wiz_hue_e_title' : 'wiz_hue_title'; - changeWizard(data, hue_title, startWizardPhilipsHue); - }); - $("#root_specificOptions_useEntertainmentAPI").trigger("change"); + var ledWizardType = ledType; + var data = { type: ledWizardType }; + var hue_title = 'wiz_hue_title'; + changeWizard(data, hue_title, startWizardPhilipsHue); } else if (ledType == "atmoorb") { var ledWizardType = (this.checked) ? "atmoorb" : ledType; diff --git a/assets/webconfig/js/wizard.js b/assets/webconfig/js/wizard.js index 3f88f6db..79fc9182 100755 --- a/assets/webconfig/js/wizard.js +++ b/assets/webconfig/js/wizard.js @@ -604,7 +604,7 @@ var lightPosTopLeft112 = { hmin: 0, hmax: 0.5, vmin: 0, vmax: 0.15 }; var lightPosTopLeft121 = { hmin: 0.5, hmax: 1, vmin: 0, vmax: 0.15 }; var lightPosTopLeftNewMid = { hmin: 0.25, hmax: 0.75, vmin: 0, vmax: 0.15 }; -function assignLightPos(id, pos, name) { +function assignLightPos(pos, name) { var i = null; if (pos === "top") @@ -695,52 +695,50 @@ devicesProperties = {}; var hueIPs = []; var hueIPsinc = 0; -var hueLights = null; -var hueGroups = null; +var hueLights = []; +var hueEntertainmentConfigs = []; +var hueEntertainmentServices = []; var lightLocation = []; var groupLights = []; +var groupChannels = []; var groupLightsLocations = []; -var hueType = "philipshue"; +var isAPIv2Ready = true; +var isEntertainmentReady = true; function startWizardPhilipsHue(e) { - if (typeof e.data.type != "undefined") hueType = e.data.type; - //create html var hue_title = 'wiz_hue_title'; - var hue_intro1 = 'wiz_hue_intro1'; + var hue_intro1 = 'wiz_hue_e_intro1'; var hue_desc1 = 'wiz_hue_desc1'; var hue_create_user = 'wiz_hue_create_user'; - if (hueType == 'philipshueentertainment') { - hue_title = 'wiz_hue_e_title'; - hue_intro1 = 'wiz_hue_e_intro1'; - hue_desc1 = 'wiz_hue_e_desc1'; - hue_create_user = 'wiz_hue_e_create_user'; - } + $('#wiz_header').html('' + $.i18n(hue_title)); $('#wizp1_body').html('

' + $.i18n(hue_title) + '

' + $.i18n(hue_intro1) + '

'); $('#wizp1_footer').html(''); $('#wizp2_body').html('
'); - var hidePort = "hidden-lg"; - if (storedAccess === 'expert') { - hidePort = ""; - } - - $('#wh_topcontainer').append('

' + $.i18n(hue_desc1) + '

' + + var topContainer_html = '

' + $.i18n(hue_desc1) + '

' + '
' + '
' + '

' + $.i18n('wiz_hue_ip') + '

' + '
' + ' ' + - '
' + - '
' + - ' :' + - '
' + - '

' - ); - $('#wh_topcontainer').append(); - $('#wh_topcontainer').append(''); + ' ' + '' + + '
' + + ' ' + + '
'; + + if (storedAccess === 'expert') { + topContainer_html += '
' + + ':' + + '
'; + } + + topContainer_html += '

'; + topContainer_html += ''; + + $('#wh_topcontainer').append(topContainer_html); $('#usrcont').append('

' + $.i18n('wiz_hue_username') + '

' + '
' + @@ -751,23 +749,18 @@ function startWizardPhilipsHue(e) { '
' ); - if (hueType == 'philipshueentertainment') { - $('#usrcont').append('

' + $.i18n('wiz_hue_clientkey') + - '


'); - } + $('#usrcont').append('

' + $.i18n('wiz_hue_clientkey') + + '


'); $('#usrcont').append('

<\p>' + ''); - if (hueType == 'philipshueentertainment') { - $('#wizp2_body').append('

'); - createTable("gidsh", "gidsb", "hue_grp_ids_t"); - $('.gidsh').append(createTableRow([$.i18n('edt_dev_spec_groupId_title'), $.i18n('wiz_hue_e_use_group')], true)); - $('#wizp2_body').append(''); - } - else { - $('#wizp2_body').append(''); - } + $('#wizp2_body').append(''); + createTable("gidsh", "gidsb", "hue_grp_ids_t"); + $('.gidsh').append(createTableRow([$.i18n('edt_dev_spec_groupId_title'), ""], true)); + + $('#wizp2_body').append(''); + createTable("lidsh", "lidsb", "hue_ids_t"); $('.lidsh').append(createTableRow([$.i18n('edt_dev_spec_lightid_title'), $.i18n('wiz_pos'), $.i18n('wiz_identify')], true)); $('#wizp2_footer').html(''); @@ -793,13 +786,26 @@ function startWizardPhilipsHue(e) { function checkHueBridge(cb, hueUser) { var usr = (typeof hueUser != "undefined") ? hueUser : 'config'; - if (usr == 'config') $('#wiz_hue_discovered').html(""); + if (usr === 'config') { + $('#wiz_hue_discovered').html(""); + } if (hueIPs[hueIPsinc]) { var host = hueIPs[hueIPsinc].host; var port = hueIPs[hueIPsinc].port; - getProperties_hue_bridge(cb, decodeURIComponent(host), port, usr); + if (usr != '') + { + getProperties_hue_bridge(cb, decodeURIComponent(host), port, usr); + } + else + { + cb(false, usr); + } + + if (isAPIv2Ready) { + $('#port').val(443); + } } } @@ -811,37 +817,51 @@ function checkBridgeResult(reply, usr) { $('#port').val(hueIPs[hueIPsinc].port) $('#usrcont').toggle(true); - checkHueBridge(checkUserResult, $('#user').val() ? $('#user').val() : "newdeveloper"); + + checkHueBridge(checkUserResult, $('#user').val()); } else { - //increment and check again - if (hueIPs.length - 1 > hueIPsinc) { - hueIPsinc++; - checkHueBridge(checkBridgeResult); - } - else { - $('#usrcont').toggle(false); - $('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip')); - } + $('#usrcont').toggle(false); + $('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip')); } }; -function checkUserResult(reply, usr) { +function checkUserResult(reply, username) { $('#usrcont').toggle(true); - if (reply) { - $('#user').val(usr); - if (hueType == 'philipshueentertainment' && $('#clientkey').val() == "") { + var hue_create_user = 'wiz_hue_e_create_user'; + if (!isEntertainmentReady) { + hue_create_user = 'wiz_hue_create_user'; + $('#hue_client_key_r').toggle(false); + } else { + $('#hue_client_key_r').toggle(true); + } + + $('#wiz_hue_create_user').text($.i18n(hue_create_user)); + $('#wiz_hue_create_user').toggle(true); + + if (reply) { + $('#user').val(username); + + if (isEntertainmentReady && $('#clientkey').val() == "") { $('#wiz_hue_usrstate').html($.i18n('wiz_hue_e_clientkey_needed')); $('#wiz_hue_create_user').toggle(true); } else { $('#wiz_hue_usrstate').html(""); $('#wiz_hue_create_user').toggle(false); - if (hueType == 'philipshue') { - get_hue_lights(); - } - if (hueType == 'philipshueentertainment') { - get_hue_groups(); + + if (isEntertainmentReady) { + $('#hue_id_headline').text($.i18n('wiz_hue_e_desc3')); + $('#hue_grp_ids_t').toggle(true); + + get_hue_groups(username); + + } else { + $('#hue_id_headline').text($.i18n('wiz_hue_desc2')); + $('#hue_grp_ids_t').toggle(false); + + get_hue_lights(username); + } } } @@ -852,22 +872,73 @@ function checkUserResult(reply, usr) { } }; -function useGroupId(id) { - $('#groupId').val(id); +function useGroupId(id, username) { + $('#groupId').val(hueEntertainmentConfigs[id].id); + if (isAPIv2Ready) { + var group = hueEntertainmentConfigs[id]; - //Ensure ligthIDs are strings - groupLights = hueGroups[id].lights.map(num => { - return String(num); - }); + groupLights = []; + for (const light of group.light_services) { + groupLights.push(light.rid); + } - groupLightsLocations = hueGroups[id].locations; - get_hue_lights(); + groupChannels = []; + for (const channel of group.channels) { + groupChannels.push(channel); + } + + groupLightsLocations = []; + for (const location of group.locations.service_locations) { + groupLightsLocations.push(location); + } + } else { + //Ensure ligthIDs are strings + groupLights = hueEntertainmentConfigs[id].lights.map(num => { + return String(num); + }); + + var lightLocations = hueEntertainmentConfigs[id].locations; + for (var locationID in lightLocations) { + var lightLocation = {}; + + let position = { + x: lightLocations[locationID][0], + y: lightLocations[locationID][1], + z: lightLocations[locationID][2] + }; + lightLocation.position = position; + + groupLightsLocations.push(lightLocation); + } + } + + get_hue_lights(username); } +function updateBridgeDetails(properties) { + var ledDeviceProperties = properties.config; + + if (!jQuery.isEmptyObject(ledDeviceProperties)) { + isEntertainmentReady = properties.isEntertainmentReady; + isAPIv2Ready = properties.isAPIv2Ready; + + if (ledDeviceProperties.name && ledDeviceProperties.bridgeid && ledDeviceProperties.modelid) { + $('#wiz_hue_discovered').html( + "Bridge: " + ledDeviceProperties.name + + ", Modelid: " + ledDeviceProperties.modelid + + ", Firmware: " + ledDeviceProperties.swversion + "
" + + "API-Version: " + ledDeviceProperties.apiversion + + ", Entertainment: " + (isEntertainmentReady ? "✓" : "-") + + ", APIv2: " + (isAPIv2Ready ? "✓" : "-") + ); + } + } +} async function discover_hue_bridges() { $('#wiz_hue_ipstate').html($.i18n('edt_dev_spec_devices_discovery_inprogress')); - $('#wiz_hue_discovered').html("") + + // $('#wiz_hue_discovered').html("") const res = await requestLedDeviceDiscovery('philipshue'); if (res && !res.error) { const r = res.info; @@ -903,11 +974,6 @@ async function discover_hue_bridges() { port = device.port; } - //Remap https port to http port until Hue-API v2 is supported - if (port == 443) { - port = 80; - } - if (host) { if (!hueIPs.some(item => item.host === host)) { @@ -916,22 +982,39 @@ async function discover_hue_bridges() { } } } + $('#wiz_hue_ipstate').html(""); $('#host').val(hueIPs[hueIPsinc].host) $('#port').val(hueIPs[hueIPsinc].port) - var usr = $('#user').val(); - if (usr != "") { - checkHueBridge(checkUserResult, usr); - } else { - checkHueBridge(checkBridgeResult); + $('#hue_bridge_select').html(""); + + for (var key in hueIPs) { + $('#hue_bridge_select').append(createSelOpt(key, hueIPs[key].host)); } + + $('.hue_bridge_sel_watch').on("click", function () { + hueIPsinc = $(this).val(); + + var name = $("#hue_bridge_select option:selected").text(); + $('#host').val(name); + $('#port').val(hueIPs[hueIPsinc].port) + + var usr = $('#user').val(); + if (usr != "") { + checkHueBridge(checkUserResult, usr); + } else { + checkHueBridge(checkBridgeResult); + } + }); + + $('.hue_bridge_sel_watch').click(); } } } async function getProperties_hue_bridge(cb, hostAddress, port, username, resourceFilter) { - let params = { host: hostAddress, user: username, filter: resourceFilter }; + let params = { host: hostAddress, username: username, filter: resourceFilter }; if (port !== 'undefined') { params.port = parseInt(port); } @@ -945,23 +1028,27 @@ async function getProperties_hue_bridge(cb, hostAddress, port, username, resourc } // Use device's properties, if properties in chache - if (devicesProperties[ledType][key]) { + if (devicesProperties[ledType][key] && devicesProperties[ledType][key][username]) { + updateBridgeDetails(devicesProperties[ledType][key]); cb(true, username); } else { const res = await requestLedDeviceProperties(ledType, params); - - if (res && !res.error) { var ledDeviceProperties = res.info.properties; if (!jQuery.isEmptyObject(ledDeviceProperties)) { + devicesProperties[ledType][key] = {}; + devicesProperties[ledType][key][username] = ledDeviceProperties; + + isAPIv2Ready = res.info.isAPIv2Ready; + devicesProperties[ledType][key].isAPIv2Ready = isAPIv2Ready; + isEntertainmentReady = res.info.isEntertainmentReady; + devicesProperties[ledType][key].isEntertainmentReady = isEntertainmentReady; + + updateBridgeDetails(devicesProperties[ledType][key]); if (username === "config") { - if (ledDeviceProperties.name && ledDeviceProperties.bridgeid && ledDeviceProperties.modelid) { - $('#wiz_hue_discovered').html("Bridge: " + ledDeviceProperties.name + ", Modelid: " + ledDeviceProperties.modelid + ", API-Version: " + ledDeviceProperties.apiversion); - cb(true); - } + cb(true); } else { - devicesProperties[ledType][key] = ledDeviceProperties; cb(true, username); } } else { @@ -973,12 +1060,12 @@ async function getProperties_hue_bridge(cb, hostAddress, port, username, resourc } } -async function identify_hue_device(hostAddress, port, username, id) { +async function identify_hue_device(hostAddress, port, username, name, id, id_v1) { var disabled = $('#btn_wiz_save').is(':disabled'); // Take care that new record cannot be save during background process $('#btn_wiz_save').prop('disabled', true); - let params = { host: decodeURIComponent(hostAddress), user: username, lightId: id }; + let params = { host: decodeURIComponent(hostAddress), username: username, lightName: decodeURIComponent(name), lightId: id, lightId_v1: id_v1 }; if (port !== 'undefined') { params.port = parseInt(port); @@ -1003,11 +1090,9 @@ function beginWizardHue() { $('#user').val(usr); } - if (hueType == 'philipshueentertainment') { - var clkey = eV("clientkey"); - if (clkey != "") { - $('#clientkey').val(clkey); - } + var clkey = eV("clientkey"); + if (clkey != "") { + $('#clientkey').val(clkey); } //check if host is empty/reachable/search for bridge @@ -1022,13 +1107,13 @@ function beginWizardHue() { $('#host').val(host); var port = eV("port"); - if (port == 0) { - $('#port').val(80); - } - else { + if (port > 0) { $('#port').val(port); } - hueIPs.unshift({ host: host, port: port }); + else { + $('#port').val(''); + } + hueIPs.push({ host: host, port: port }); if (usr != "") { checkHueBridge(checkUserResult, usr); @@ -1038,18 +1123,18 @@ function beginWizardHue() { } $('#retry_bridge').off().on('click', function () { + var host = $('#host').val(); + var port = parseInt($('#port').val()); - if ($('#host').val() != "") { + if (host != "") { - hueIPs = []; - hueIPsinc = 0; - - var port = $('#port').val(); - if (isNaN(port) || port < 1 || port > 65535) { - port = 80; - $('#port').val(80); + var idx = hueIPs.findIndex(item => item.host === host && item.port === port); + if (idx === -1) { + hueIPs.push({ host: host, port: port }); + hueIPsinc = hueIPs.length - 1; + } else { + hueIPsinc = idx; } - hueIPs.push({ host: $('#host').val(), port: port }); } else { discover_hue_bridges(); @@ -1064,29 +1149,177 @@ function beginWizardHue() { }); $('#retry_usr').off().on('click', function () { - checkHueBridge(checkUserResult, $('#user').val() ? $('#user').val() : "newdeveloper"); + checkHueBridge(checkUserResult, $('#user').val()); }); $('#wiz_hue_create_user').off().on('click', function () { - if ($('#host').val() != "") { - hueIPs.unshift({ host: $('#host').val(), port: $('#port').val() }); - } createHueUser(); }); + function assignLightEntertainmentPos(isFocusCenter, position, name, id) { + + var x = position.x; + var z = position.z; + + if (isFocusCenter) { + // Map lights as in centered range -0.5 to 0.5 + if (x < -0.5) { + x = -0.5; + } else if (x > 0.5) { + x = 0.5; + } + if (z < -0.5) { + z = -0.5; + } else if (z > 0.5) { + z = 0.5; + } + } else { + // Map lights as in full range -1 to 1 + x /= 2; + z /= 2; + } + + var h = x + 0.5; + var v = -z + 0.5; + + var hmin = h - 0.05; + var hmax = h + 0.05; + var vmin = v - 0.05; + var vmax = v + 0.05; + + let layoutObject = { + hmin: hmin < 0 ? 0 : hmin, + hmax: hmax > 1 ? 1 : hmax, + vmin: vmin < 0 ? 0 : vmin, + vmax: vmax > 1 ? 1 : vmax, + name: name + }; + + if (id) { + layoutObject.name += "_" + id; + } + return layoutObject; + } + + function assignSegmentedLightPos(segment, position, name) { + var layoutObjects = []; + + var segTotalLength = 0; + for (var key in segment) { + + segTotalLength += segment[key].length; + } + + var min; + var max; + var horizontal = true; + + var layoutObject = assignLightPos(position, name); + if (position === "left" || position === "right") { + // vertical distribution + min = layoutObject.vmin; + max = layoutObject.vmax; + horizontal = false; + + } else { + // horizontal distribution + min = layoutObject.hmin; + max = layoutObject.hmax; + } + + var step = (max - min) / segTotalLength; + var start = min; + + for (var key in segment) { + min = start; + max = round(start + segment[key].length * step); + + if (horizontal) { + layoutObject.hmin = min; + layoutObject.hmax = max; + } else { + layoutObject.vmin = min; + layoutObject.vmax = max; + } + layoutObject.name = name + "_" + key; + layoutObjects.push(JSON.parse(JSON.stringify(layoutObject))); + + start = max; + } + + return layoutObjects; + } + $('#btn_wiz_save').off().on("click", function () { var hueLedConfig = []; var finalLightIds = []; + var channelNumber = 0; //create hue led config - for (var key in hueLights) { - if (hueType == 'philipshueentertainment') { - if (groupLights.indexOf(key) == -1) continue; - } - if ($('#hue_' + key).val() != "disabled") { - finalLightIds.push(key); - var idx_content = assignLightPos(key, $('#hue_' + key).val(), hueLights[key].name); - hueLedConfig.push(JSON.parse(JSON.stringify(idx_content))); + for (var key in groupLights) { + var lightId = groupLights[key]; + + if ($('#hue_' + lightId).val() != "disabled") { + finalLightIds.push(lightId); + + var lightName; + if (isAPIv2Ready) { + var light = hueLights.find(light => light.id === lightId); + lightName = light.metadata.name; + } else { + lightName = hueLights[lightId].name; + } + + var position = $('#hue_' + lightId).val(); + var lightIdx = groupLights.indexOf(lightId); + var lightLocation = groupLightsLocations[lightIdx]; + + var serviceID; + if (isAPIv2Ready) { + serviceID = lightLocation.service.rid; + } + + if (position.startsWith("entertainment")) { + + // Layout per entertainment area definition at bridge + var isFocusCenter = false; + if (position === "entertainment_center") { + isFocusCenter = true; + } + + if (isAPIv2Ready) { + + groupChannels.forEach((channel) => { + if (channel.members[0].service.rid === serviceID) { + var layoutObject = assignLightEntertainmentPos(isFocusCenter, channel.position, lightName, channel.channel_id); + hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject))); + ++channelNumber; + } + }); + } else { + var layoutObject = assignLightEntertainmentPos(isFocusCenter, lightLocation.position, lightName); + hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject))); + } + } + else { + // Layout per manual settings + var maxSegments = 1; + + if (isAPIv2Ready) { + var service = hueEntertainmentServices.find(service => service.id === serviceID); + maxSegments = service.segments.max_segments; + } + + if (maxSegments > 1) { + var segment = service.segments.segments; + var layoutObjects = assignSegmentedLightPos(segment, position, lightName); + hueLedConfig.push(...layoutObjects); + } else { + var layoutObject = assignLightPos(position, lightName); + hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject))); + } + channelNumber += maxSegments; + } } } @@ -1121,7 +1354,7 @@ function beginWizardHue() { d.brightnessFactor = parseFloat(eV("brightnessFactor", 1)); d.clientkey = $('#clientkey').val(); - d.groupId = parseInt($('#groupId').val()); + d.groupId = $('#groupId').val(); d.blackLightsTimeout = parseInt(eV("blackLightsTimeout", 5000)); d.brightnessMin = parseFloat(eV("brightnessMin", 0)); d.brightnessMax = parseFloat(eV("brightnessMax", 1)); @@ -1134,8 +1367,16 @@ function beginWizardHue() { d.enableAttempts = parseInt(conf_editor.getEditor("root.generalOptions.enableAttempts").getValue()); d.enableAttemptsInterval = parseInt(conf_editor.getEditor("root.generalOptions.enableAttemptsInterval").getValue()); - if (hueType == 'philipshue') { - d.useEntertainmentAPI = false; + d.useEntertainmentAPI = isEntertainmentReady; + d.useAPIv2 = isAPIv2Ready; + + if (isEntertainmentReady) { + d.hardwareLedCount = channelNumber; + if (window.serverConfig.device.type !== d.type) { + //smoothing on, if new device + sc.smoothing = { enable: true }; + } + } else { d.hardwareLedCount = finalLightIds.length; d.verbose = false; if (window.serverConfig.device.type !== d.type) { @@ -1144,15 +1385,6 @@ function beginWizardHue() { } } - if (hueType == 'philipshueentertainment') { - d.useEntertainmentAPI = true; - d.hardwareLedCount = groupLights.length; - if (window.serverConfig.device.type !== d.type) { - //smoothing on, if new device - sc.smoothing = { enable: true }; - } - } - window.serverConfig.device = d; requestWriteConfig(sc, true); @@ -1163,7 +1395,6 @@ function beginWizardHue() { } function createHueUser() { - var host = hueIPs[hueIPsinc].host; var port = hueIPs[hueIPsinc].port; @@ -1208,7 +1439,8 @@ function createHueUser() { conf_editor.getEditor("root.specificOptions.host").setValue(host); conf_editor.getEditor("root.specificOptions.port").setValue(port); } - if (hueType == 'philipshueentertainment') { + + if (isEntertainmentReady) { var clientkey = response.clientkey; if (clientkey != 'undefined') { $('#clientkey').val(clientkey); @@ -1230,37 +1462,52 @@ function createHueUser() { }, retryInterval * 1000); } -function get_hue_groups() { - +function get_hue_groups(username) { var host = hueIPs[hueIPsinc].host; - if (devicesProperties['philipshue'][host]) { - var ledProperties = devicesProperties['philipshue'][host]; + if (devicesProperties['philipshue'][host] && devicesProperties['philipshue'][host][username]) { + var ledProperties = devicesProperties['philipshue'][host][username]; - if (!jQuery.isEmptyObject(ledProperties)) { - hueGroups = ledProperties.groups; - if (Object.keys(hueGroups).length > 0) { - - $('.lidsb').html(""); - $('#wh_topcontainer').toggle(false); - $('#hue_grp_ids_t').toggle(true); - - var gC = 0; - for (var groupid in hueGroups) { - if (hueGroups[groupid].type == 'Entertainment') { - $('.gidsb').append(createTableRow([groupid + ' (' + hueGroups[groupid].name + ')', ''])); - gC++; - } - } - if (gC == 0) { - noAPISupport('wiz_hue_e_noegrpids'); + if (isAPIv2Ready) { + if (!jQuery.isEmptyObject(ledProperties.data)) { + if (Object.keys(ledProperties.data).length > 0) { + hueEntertainmentConfigs = ledProperties.data.filter(config => { + return config.type === "entertainment_configuration"; + }); + hueEntertainmentServices = ledProperties.data.filter(config => { + return (config.type === "entertainment" && config.renderer === true); + }); } } + } else { + if (!jQuery.isEmptyObject(ledProperties.groups)) { + hueEntertainmentConfigs = []; + var hueGroups = ledProperties.groups; + for (var groupid in hueGroups) { + if (hueGroups[groupid].type == 'Entertainment') { + hueGroups[groupid].id = groupid; + hueEntertainmentConfigs.push(hueGroups[groupid]); + } + } + } + } + + if (Object.keys(hueEntertainmentConfigs).length > 0) { + + $('.lidsb').html(""); + $('#wh_topcontainer').toggle(false); + $('#hue_grp_ids_t').toggle(true); + + for (var groupid in hueEntertainmentConfigs) { + $('.gidsb').append(createTableRow([groupid + ' (' + hueEntertainmentConfigs[groupid].name + ')', ''])); + } + } else { + noAPISupport('wiz_hue_e_noegrpids', username); } } } -function noAPISupport(txt) { +function noAPISupport(txt, username) { showNotification('danger', $.i18n('wiz_hue_e_title'), $.i18n('wiz_hue_e_noapisupport_hint')); conf_editor.getEditor("root.specificOptions.useEntertainmentAPI").setValue(false); $("#root_specificOptions_useEntertainmentAPI").trigger("change"); @@ -1269,51 +1516,82 @@ function noAPISupport(txt) { var txt = (txt) ? $.i18n(txt) : $.i18n('wiz_hue_e_nogrpids'); $('

' + txt + '
' + $.i18n('wiz_hue_e_noapisupport') + '

').insertBefore('#wizp2_body #hue_ids_t'); $('#hue_id_headline').html($.i18n('wiz_hue_desc2')); - hueType = 'philipshue'; - get_hue_lights(); + + get_hue_lights(username); } -function get_hue_lights() { - +function get_hue_lights(username) { var host = hueIPs[hueIPsinc].host; - if (devicesProperties['philipshue'][host]) { - var ledProperties = devicesProperties['philipshue'][host]; + if (devicesProperties['philipshue'][host] && devicesProperties['philipshue'][host][username]) { + var ledProperties = devicesProperties['philipshue'][host][username]; - if (!jQuery.isEmptyObject(ledProperties.lights)) { - hueLights = ledProperties.lights; - if (Object.keys(hueLights).length > 0) { - if (hueType == 'philipshue') { - $('#wh_topcontainer').toggle(false); + if (isAPIv2Ready) { + if (!jQuery.isEmptyObject(ledProperties.data)) { + if (Object.keys(ledProperties.data).length > 0) { + hueLights = ledProperties.data.filter(config => { + return config.type === "light"; + }); } - $('#hue_ids_t, #btn_wiz_save').toggle(true); + } + } else { + if (!jQuery.isEmptyObject(ledProperties.lights)) { + hueLights = ledProperties.lights; + } + } - var lightOptions = [ - "top", "topleft", "topright", - "bottom", "bottomleft", "bottomright", - "left", "lefttop", "leftmiddle", "leftbottom", - "right", "righttop", "rightmiddle", "rightbottom", - "entire", - "lightPosTopLeft112", "lightPosTopLeftNewMid", "lightPosTopLeft121", - "lightPosBottomLeft14", "lightPosBottomLeft12", "lightPosBottomLeft34", "lightPosBottomLeft11", - "lightPosBottomLeft112", "lightPosBottomLeftNewMid", "lightPosBottomLeft121" - ]; + if (Object.keys(hueLights).length > 0) { + if (!isEntertainmentReady) { + $('#wh_topcontainer').toggle(false); + } + $('#hue_ids_t, #btn_wiz_save').toggle(true); - if (hueType == 'philipshue') { - lightOptions.unshift("disabled"); + var lightOptions = [ + "top", "topleft", "topright", + "bottom", "bottomleft", "bottomright", + "left", "lefttop", "leftmiddle", "leftbottom", + "right", "righttop", "rightmiddle", "rightbottom", + "entire", + "lightPosTopLeft112", "lightPosTopLeftNewMid", "lightPosTopLeft121", + "lightPosBottomLeft14", "lightPosBottomLeft12", "lightPosBottomLeft34", "lightPosBottomLeft11", + "lightPosBottomLeft112", "lightPosBottomLeftNewMid", "lightPosBottomLeft121" + ]; + + if (isEntertainmentReady) { + lightOptions.unshift("entertainment_center"); + lightOptions.unshift("entertainment"); + } else { + lightOptions.unshift("disabled"); + groupLights = Object.keys(hueLights); + } + + $('.lidsb').html(""); + + var pos = ""; + for (var id in groupLights) { + var lightId = groupLights[id]; + var lightId_v1 = "/lights/" + lightId; + + var lightName; + if (isAPIv2Ready) { + var light = hueLights.find(light => light.id === lightId); + lightName = light.metadata.name; + lightId_v1 = light.id_v1; + } else { + lightName = hueLights[lightId].name; } - $('.lidsb').html(""); - var pos = ""; - for (var lightid in hueLights) { - if (hueType == 'philipshueentertainment') { - if (groupLights.indexOf(lightid) == -1) continue; + if (isEntertainmentReady) { + var lightLocation = {}; + lightLocation = groupLightsLocations[id]; + if (lightLocation) { + if (isAPIv2Ready) { + pos = 0; + } else { + var x = lightLocation.position.x; + var y = lightLocation.position.y; + var z = lightLocation.position.z; - if (groupLightsLocations.hasOwnProperty(lightid)) { - lightLocation = groupLightsLocations[lightid]; - var x = lightLocation[0]; - var y = lightLocation[1]; - var z = lightLocation[2]; var xval = (x < 0) ? "left" : "right"; if (z != 1 && x >= -0.25 && x <= 0.25) xval = ""; switch (z) { @@ -1329,37 +1607,39 @@ function get_hue_lights() { } } } - var options = ""; - for (var opt in lightOptions) { - var val = lightOptions[opt]; - var txt = (val != 'entire' && val != 'disabled') ? 'conf_leds_layout_cl_' : 'wiz_ids_'; - options += '
-
-
-
-
-

-

- -
- + diff --git a/assets/webconfig/i18n/he.json b/assets/webconfig/i18n/he.json new file mode 100644 index 00000000..df977a15 --- /dev/null +++ b/assets/webconfig/i18n/he.json @@ -0,0 +1,155 @@ +{ + "InfoDialog_access_text": "בהתאם לרמת ההגדרות תוכל להתאים אפשרויות נוספות או לקבל גישה לתכונות נוספות. רמת \"ברירת המחדל\" המומלצת היא.", + "InfoDialog_iswitch_text": "אם אתה מפעיל את Hyperion יותר מפעם אחת ברשת המקומית שלך, תוכל לעבור בין תצורות האינטרנט. בחר את המופע של Hyperion למטה והחלף!", + "InfoDialog_nostorage_text": "הדפדפן שלך אינו תומך ב-localStorage. לא ניתן לשמור הגדרת שפה מסוימת (חזרה ל'זיהוי אוטומטי') ורמת גישה (חזרה ל'ברירת מחדל'). קוסמים מסוימים עשויים להיות מוסתרים. אתה עדיין יכול להשתמש בממשק האינטרנט ללא בעיות נוספות", + "InfoDialog_nowrite_text": "Hyperion לא יכול לכתוב לקובץ התצורה הנטען הנוכחי שלך. אנא תקן את הרשאות הקובץ כדי להמשיך.", + "conf_colors_blackborder_intro": "דלג על פסים שחורים באשר הם. כל מצב משתמש באלגוריתם זיהוי אחר המכוון למצבים מיוחדים. תעלה את הסף אם זה לא עובד לך.", + "conf_colors_color_intro": "צור פרופיל כיול אחד או יותר, התאם כל צבע, בהירות, ליניאריזציה ועוד.", + "conf_colors_smoothing_intro": "החלקה משטחת שינויי צבע/בהירות כדי להפחית הסחת דעת מעצבנת.", + "conf_effect_bgeff_intro": "הגדר אפקט/צבע רקע, המוצג בזמן \"בטלה\" של Hyperion. מתחיל תמיד בערוץ עדיפות 255.", + "conf_effect_fgeff_intro": "הגדר אפקט אתחול או צבע, שיוצג במהלך האתחול של Hyperion למשך הזמן שהוגדר.", + "conf_effect_path_intro": "טען אפקטים מהנתיבים המוגדרים. בנוסף, אתה יכול להשבית אפקטים בודדים לפי שם כדי להסתיר אותם מכל רשימות האפקטים.", + "conf_general_impexp_expbtn": "ייצא", + "conf_general_impexp_impbtn": "ייבא", + "conf_general_impexp_l1": "ייבא הגדרה על ידי בחירת קובץ הגדרות למטה ולחץ על \"ייבוא\".", + "conf_general_impexp_l2": "ייצא הגדרה על ידי בחירת קובץ הגדרות למטה ולחץ על \"ייצוא\".", + "conf_general_impexp_title": "תצורת ייבוא/ייצוא", + "conf_general_intro": "הגדרות בסיסיות סביב Hyperion ו-WebUI שאינן מתאימות לקטגוריה אחרת.", + "conf_general_label_title": "הגדרות כלליות", + "conf_grabber_fg_intro": "לכידת מסך היא לכידת המערכת המקומית שלך כמקור קלט, Hyperion מותקן על.", + "conf_grabber_v4l_intro": "לכידת USB היא התקן (לכידה) המחובר באמצעות USB המשמש להזנת תמונות מקור לעיבוד.", + "conf_helptable_expl": "הֶסבֵּר", + "conf_helptable_option": "אפשרות", + "conf_leds_contr_label_contrtype": "סוג בקר", + "conf_leds_device_intro": "Hyperion תומך בהרבה בקרים להעברת נתונים למכשיר היעד שלך. בחר בקר LED מתוך הרשימה הממוינת והגדר אותו. בחרנו את הגדרות ברירת המחדל הטובות ביותר עבור כל מכשיר.", + "conf_leds_layout_btn_checklist": "הצג רשימת בדיקה", + "conf_leds_layout_checkp1": "ה-LED השחור הוא ה-LED הראשון שלך, LED הראשון הוא הנקודה שבה אתה מזין את אות הנתונים שלך.", + "conf_leds_layout_checkp2": "הפריסה היא תמיד המראה הקדמי של הטלוויזיה שלך, לעולם לא התצוגה האחורית.", + "conf_leds_layout_checkp3": "ודא שהכיוון נכון. הנוריות האפורות מציינות את מספר LED 2 ו-3 כדי להמחיש את כיוון הנתונים.", + "conf_leds_layout_checkp4": "בחר פער: בכדי ליצור פער, התעלם ממנו תחילה כאשר אתה מגדיר עליון/תחתון/שמאל/ימין והגדר לאחר מכן את אורך הפער כדי להסיר כמות לדים. שנה את מיקום הפער עד שיתאים.", + "conf_leds_layout_frame": "פריסה קלאסית (מסגרת LED)", + "conf_leds_layout_generatedconf": "תצורת LED שנוצרה/נוכחית", + "conf_leds_layout_intro": "אתה גם צריך פריסת LED, המשקפת את עמדות LED שלך. הפריסה הקלאסית היא מסגרת הטלוויזיה המשמשת בדרך כלל, אך אנו תומכים גם ביצירת מטריצות LED (קירות LED). התצוגה על פריסה זו היא תמיד מחזית הטלוויזיה שלך.", + "conf_leds_layout_matrix": "פריסת מטריצה (קיר LED)", + "conf_leds_layout_textf1": "שדה טקסט זה מציג כברירת מחדל את הפריסה הנטענת הנוכחית שלך וייחלף אם תיצור פריסה חדשה עם האפשרויות שלמעלה. לחלופין, תוכל לבצע עריכות נוספות.", + "conf_leds_nav_label_ledcontroller": "בקר LED", + "conf_leds_nav_label_ledlayout": "פריסת LED", + "conf_leds_optgroup_network": "רשת", + "conf_logging_label_intro": "אזור לבדיקת הודעות יומן, תראה יותר או פחות מידע בהתאם לרמת הרישום שנקבעה.", + "conf_network_forw_intro": "העבר את כל הקלט להתקנה שנייה של Hyperion שניתן להניע עם בקר LED אחר", + "conf_network_proto_intro": "ה-PROTO-Port של כל המופעים של Hyperion, המשמש עבור זרמי תמונות (HyperionScreenCap, Kodi Addon, Android Hyperion Grabber, ...)", + "dashboard_alert_message_confedit": "תצורת ה-Hyperion שלך שונתה. כדי להחיל אותו, הפעל מחדש את Hyperion.", + "dashboard_alert_message_confedit_t": "התצורה השתנתה", + "dashboard_alert_message_confsave_success": "תצורת ה-Hyperion שלך נשמרה בהצלחה. השינויים שלך פעילים כעת.", + "dashboard_alert_message_confsave_success_t": "התצורה נשמרה", + "dashboard_componentbox_label_comp": "רְכִיב", + "dashboard_componentbox_label_status": "מצב", + "dashboard_componentbox_label_title": "מצב הרכיבים", + "dashboard_infobox_label_currenthyp": "גרסת ה-Hyperion שלך:", + "dashboard_infobox_label_instance": "למשל:", + "dashboard_infobox_label_latesthyp": "הגרסה האחרונה של Hyperion:", + "dashboard_infobox_label_platform": "פלטפורמה:", + "dashboard_infobox_label_ports": "יציאות", + "dashboard_infobox_label_smartacc": "גישה חכמה", + "dashboard_infobox_label_statush": "מצב ה-Hyperion:", + "dashboard_infobox_label_title": "מידע", + "dashboard_infobox_message_updatesuccess": "הינך מריץ את הגרסה האחרונה של Hyperion.", + "dashboard_infobox_message_updatewarning": "גרסה חדשה יותר של Hyperion זמינה! ($1)", + "dashboard_label_intro": "לוח המחוונים נותן לך סקירה מהירה על מצב ה- Hyperion", + "dashboard_newsbox_label_title": "בלוג ה-Hyperion", + "dashboard_newsbox_noconn": "לא ניתן להתחבר לשרת Hyperion כדי לאחזר את הפוסטים האחרונים, האם חיבור האינטרנט שלך תקין?", + "dashboard_newsbox_readmore": "קרא עוד", + "dashboard_newsbox_visitblog": "בקר ב-Hyperion בלוג ", + "edt_conf_color_brightnessComp_expl": "מפצה על הבדלי בהירות בין אדום ירוק כחול, ציאן מגנטה צהוב ולבן. 100 פירושו פיצוי מלא, 0 אין פיצוי", + "edt_conf_color_channelAdjustment_header_expl": "צור פרופילי צבע שניתן להקצות לרכיב מסוים. התאם צבע, גמא, בהירות, פיצוי ועוד.", + "edt_conf_v4l2_fpsSoftwareDecimation_expl": "כדי לחסוך במשאבים כל מסגרת n' תעובד בלבד. למשל. אם ה-grabber מוגדר ל-30fps עם אפשרות זו מוגדרת ל-5, התוצאה הסופית תהיה בסביבות 6fps", + "edt_conf_v4l2_signalDetection_expl": "אם מופעל, לכידת USB תושבת זמנית כאשר לא נמצא אות. זה יקרה כאשר התמונה תרד מתחת לערך הסף למשך תקופה של 4 שניות.", + "effectsconfigurator_label_intro": "צור מתוך האפקטים הבסיסיים אפקטים חדשים המותאמים לטעמך. בהתאם לאפקט יש אפשרויות כמו צבע, מהירות, כיוון ועוד.", + "general_access_advanced": "מתקדם", + "general_access_default": "ברירת מחדל", + "general_access_expert": "מומחה", + "general_btn_back": "חזור", + "general_btn_cancel": "בטל", + "general_btn_continue": "המשך", + "general_btn_iswitch": "החלף", + "general_btn_next": "הבא", + "general_btn_off": "כיבוי", + "general_btn_ok": "בסדר", + "general_btn_on": "הדלקה", + "general_btn_restarthyperion": "הפעל מחדש את Hyperion", + "general_btn_save": "שמור", + "general_btn_saveandreload": "שמור וטען מחדש", + "general_btn_yes": "כן", + "general_button_savesettings": "שמור הגדרות", + "general_col_blue": "כחול", + "general_col_green": "ירוק", + "general_col_red": "אדום", + "general_comp_BLACKBORDER": "Blackbar זיהוי", + "general_comp_BOBLIGHTSERVER": "שרת Boblight", + "general_comp_FLATBUFSERVER": "שרת ", + "general_comp_FORWARDER": "שילוח", + "general_comp_GRABBER": "לכידת מסך", + "general_comp_LEDDEVICE": "פלט LED", + "general_comp_PROTOSERVER": "שרת מאגר פרוטוקול", + "general_comp_SMOOTHING": "חלק", + "general_comp_V4L": "לכידת כניסת USB", + "general_country_de": "גרמניה", + "general_country_es": "ספרד", + "general_country_fr": "צרפת", + "general_country_it": "איטליה", + "general_country_nl": "הולנד", + "general_country_uk": "בריטניה", + "general_country_us": "ארצות הברית", + "general_speech_cs": "צ'כית", + "general_speech_de": "גרמנית", + "general_speech_en": "אנגלית", + "general_speech_es": "ספרדית", + "general_speech_it": "איטלקית", + "general_webui_title": "Hyperion - תצורת אינטרנט", + "general_wiki_moreto": "מידע נוסף על \"$1\" בוויקי שלנו", + "info_restart_contus": "אם אתה עדיין משוטט כאן לאחר 20 שניות ואין לך מושג למה, בבקשה פתח נושא חדש בפורום התמיכה שלנו...", + "main_ledsim_btn_togglelednumber": "מספר נורות LED", + "main_ledsim_btn_toggleleds": "הצג אורות", + "main_ledsim_btn_togglelivevideo": "וידאו חי", + "main_ledsim_text": "הדמיה חיה של צבעי LED ואפשרות הזרמת הווידאו הנוכחי של מכשיר הלכידה שלך.", + "main_ledsim_title": "חזותיות LED", + "main_menu_about_token": "אודות Hyperion", + "main_menu_colors_conf_token": "עיבוד תמונה", + "main_menu_dashboard_token": "לוּחַ מַחווָנִים", + "main_menu_effect_conf_token": "אפקטים", + "main_menu_effectsconfigurator_token": "הגדרת אפקטים", + "main_menu_general_conf_token": "כללי", + "main_menu_grabber_conf_token": "לכידת חומרה", + "main_menu_input_selection_token": "בחירת קלט", + "main_menu_leds_conf_token": "יצאת LED", + "main_menu_network_conf_token": "שירותי אינטרנט", + "main_menu_remotecontrol_token": "שלט רחוק", + "main_menu_support_token": "תמיכה", + "main_menu_system_token": "מערכת", + "main_menu_update_token": "עדכון", + "main_menu_webconfig_token": "תצורת אינטרנט", + "remote_input_intro": "Hyperion משתמש במערכת עדיפות לבחירת מקור. לכל מה שתגדירו יש עדיפות (אפקט/צבע/צילום מסך/לכידת USB ומקורות רשת). כברירת מחדל, Hyperion בוחר מקורות בהתאם לעדיפות (המספר הנמוך ביותר משקף את המקור הפעיל הנוכחי). כעת יש לך הזדמנות לבחור מקורות בעצמך. $1", + "support_label_intro": "Hyperion היא תוכנה חינמית ללא מטרות רווח. צוות קטן עובד על זה וזו הסיבה שאנחנו צריכים את התמיכה הקבועה שלכם.", + "update_label_intro": "סקירה כללית על כל גרסאות Hyperion הזמינות. בנוסף, תוכל לעדכן או לשדרג לאחור את הגרסה שלך של Hyperion מתי שתרצה. ממוין מהחדש להכי ישן", + "wiz_atmoorb_desc2": "כעת בחר אילו כדורים יש להוסיף. המיקום מקצה את המנורה למיקום מסוים ב\"תמונה\" שלך. מנורות מושבתות לא יתווספו. לזיהוי מנורות בודדות לחץ על הכפתור בצד ימין.", + "wiz_atmoorb_intro1": "אשף זה מגדיר את Hyperion עבור AtmoOrbs. התכונות הן הזיהוי האוטומטי של AtmoOrb, הגדרת כל אור למיקום ספציפי בתמונה שלך או השבתה וביצוע אופטימיזציה של הגדרות Hyperion באופן אוטומטי! אז בקיצור: כל מה שאתה צריך זה כמה קליקים וסיימת!", + "wiz_cc_adjustgamma": "גמא: מה שאתה צריך לעשות הוא, להתאים את רמות הגמא של כל ערוץ עד שתהיה לך אותה כמות נתפסת של כל ערוץ. רמז: ניטרלי הוא 1.0! לדוגמה, אם האפור שלך קצת אדמדם, זה אומר שאתה צריך להגדיל את הגמא האדום כדי להפחית את כמות האדום (ככל שיותר גמא, כך פחות צבע).", + "wiz_cc_adjustit": "התאם את ה-\"$1\" שלך עד שתסתדר עם זה. שימו לב: ככל שתתרחקו מערך ברירת המחדל, ספקטרום הצבעים יהיה מוגבל (גם עבור כל הצבעים שביניהם). תלוי בקול הטלוויזיה/LED", + "wiz_cc_backlight": "בנוסף, אתה יכול להגדיר תאורה אחורית כדי למיין \"צבעים רעים\" באזורים כמעט כהים או אם אתה לא אוהב את המעבר בין צבע לכבוי במהלך הצפייה. בנוסף אתה יכול להגדיר אם צריך להיות בו קצת צבע או רק לבן. זה מושבת במצב \"כבוי\", \"צבע\" ו\"אפקט\".", + "wiz_cc_intro1": "אשף זה ידריך אותך בכיול ה-LED שלך. אם אתה משתמש בקודי, ניתן לשלוח את תמונות הכיול וסרטוני הווידאו ישירות אליו. תנאי מוקדם: עליך להפעיל את \"אפשר שליטה מרחוק מיישומים במערכות אחרות\" בקודי.
לחלופין, ייתכן שתרצה להוריד את הקבצים האלה בעצמך ולהציג אותם כשהאשף יבקש ממך להתאים את ההגדרה.", + "wiz_cc_kodidiscon": "Kodi לא נמצא, המשך ללא תמיכת Kodi (אנא בדוק אם שלט רחוק על ידי מערכות אחרות מופעלת).", + "wiz_cc_summary": "מסקנה של ההגדרות שלך. במהלך הפעלת וידאו, תוכל לשנות או לבדוק שוב ערכים. אם סיימת, לחץ על שמור.", + "wiz_cololight_intro1": "אשף זה מגדיר את Hyperion עבור מערכת Cololight. התכונות הן הזיהוי האוטומטי של Cololight וכוונון את הגדרות Hyperion באופן אוטומטי! בקיצור: כל מה שאתה צריך זה כמה קליקים וסיימת!
הערה: במקרה של Cololight Strip, ייתכן שיהיה עליך לתקן ידנית את ספירת ה-LED ואת הפריסה.", + "wiz_hue_desc1": "1. Hyperion מחפש אוטומטית עבור Hue-Bridge, במקרה שהוא לא יכול למצוא אחד, עליך לספק את שם המארח או כתובת ה-IP וללחוץ על כפתור הטעינה מחדש. \n2. ספק מזהה משתמש, אם אין לך צור אחד חדש.", + "wiz_hue_desc2": "3. כעת בחרו אילו מנורות יש להוסיף. המיקום מקצה את המנורה למיקום מסוים ב\"תמונה\" שלך. מנורות מושבתות לא יתווספו. לזיהוי מנורות בודדות לחץ על הכפתור בצד ימין", + "wiz_hue_e_desc1": "1. Hyperion מחפש אוטומטית עבור Hue-Bridge, במקרה שהוא לא יכול למצוא אחד, עליך לספק את שם המארח או כתובת ה-IP וללחוץ על כפתור הטעינה מחדש.
2. ספק מזהה משתמש ומפתח הלקוח, אם אין לך את שניהם, צור חדשים.", + "wiz_hue_e_desc3": "4. בחר באיזה מיקום המנורה המתאימה צריכה להיות \"בתמונה\". בחירה מראש של העמדה נעשתה בהתבסס על המיקומים המוגדרים של האורות בקבוצת הבידור. זו רק המלצה וניתן להתאים אישית לפי הרצון. לכן אתה יכול להדגיש אותם בקצרה על ידי לחיצה על הכפתור הימני כדי לשפר את הבחירה.", + "wiz_hue_e_intro1": "אשף זה מגדיר את Hyperion עבור מערכת Philips Hue Entertainment הידועה. התכונות הן: זיהוי אוטומטי של Hue Bridge, יצירת מפתחות משתמש ולקוח, בחירת קבוצת בידור, הגדרת אורות קבוצה למיקום ספציפי בתמונה שלך ואופטימיזציה של הגדרות Hyperion באופן אוטומטי! אז בקיצור: כל מה שאתה צריך זה כמה קליקים וסיימת!", + "wiz_hue_failure_user": "המשתמש לא נמצא, צור אחד חדש עם הכפתור למטה או הזן מזהה משתמש חוקי ולחץ על הסמל \"טען מחדש\".", + "wiz_hue_intro1": "אשף זה מגדיר את Hyperion עבור מערכת Philips Hue הידועה. התכונות הן זיהוי אוטומטי של Hue Bridge, יצירת משתמש, הגדר כל אור גוון למיקום מסוים בתמונה שלך או השבת אותו וכוונו את הגדרות Hyperion באופן אוטומטי! אז בקיצור: כל מה שאתה צריך זה כמה קליקים וסיימת!", + "wiz_rgb_expl": "נקודת הצבע מחליפה כל x שניות את הצבע (אדום, ירוק), ובאותו הזמן גם נוריות ה-LED שלך מחליפות את הצבע. ענה על השאלות בתחתית כדי לבדוק/לתקן את סדר הביטים שלך.", + "wiz_rgb_intro1": "אשף זה ידריך אותך בתהליך מציאת סדר הצבעים הנכון עבור הלדים שלך. לחץ על המשך כדי להתחיל.", + "wiz_rgb_intro2": "מתי אתה צריך את האשף הזה? דוגמה: אתה קובע את הצבע האדום, אבל אתה מקבל ירוק או כחול. אתה יכול גם להשתמש בו עבור תצורה ראשונה.", + "wiz_yeelight_desc2": "כעת בחר אילו מנורות יש להוסיף. המיקום מקצה את המנורה למיקום מסוים ב\"תמונה\" שלך. מנורות מושבתות לא יתווספו. לזיהוי מנורות בודדות לחץ על הכפתור בצד ימין.", + "wiz_yeelight_intro1": "אשף זה מגדיר את Hyperion עבור מערכת Yeelight. התכונות הן הזיהוי האוטומטי של Yeelighs, הגדרת כל אור למיקום מסוים בתמונה שלך או להשבית אותו ולכוון את הגדרות Hyperion באופן אוטומטי! אז בקיצור: כל מה שאתה צריך זה כמה קליקים וסיימת!" +} \ No newline at end of file diff --git a/assets/webconfig/js/languages.js b/assets/webconfig/js/languages.js index 21ecf191..b417b55b 100644 --- a/assets/webconfig/js/languages.js +++ b/assets/webconfig/js/languages.js @@ -1,6 +1,6 @@ var storedLang; -var availLang = ['ca', 'cs', 'da', 'de', 'el', 'en', 'es', 'fr', 'hu', 'it', 'ja', 'nl', 'nb', 'pl', 'pt', 'ro', 'sv', 'vi', 'ru', 'tr', 'zh-CN']; -var availLangText = ['Català', 'Čeština', 'Dansk', 'Deutsch', 'Ελληνική', 'English', 'Español', 'Français', 'Magyar', 'Italiano', '日本語', 'Nederlands', 'Norsk Bokmål', 'Polski', 'Português', 'Română', 'Svenska', 'Tiếng Việt', 'русский', 'Türkçe', '汉语']; +var availLang = ['ca', 'cs', 'da', 'de', 'el', 'en', 'es', 'fr', 'he', 'hu', 'it', 'ja', 'nl', 'nb', 'pl', 'pt', 'ro', 'sv', 'vi', 'ru', 'tr', 'zh-CN']; +var availLangText = ['Català', 'Čeština', 'Dansk', 'Deutsch', 'Ελληνική', 'English', 'Español', 'Français', 'עִברִית' ,'Magyar', 'Italiano', '日本語', 'Nederlands', 'Norsk Bokmål', 'Polski', 'Português', 'Română', 'Svenska', 'Tiếng Việt', 'русский', 'Türkçe', '汉语']; //$.i18n.debug = true; diff --git a/bin/scripts/install_pr.sh b/bin/scripts/install_pr.sh index bab790a0..1f654f23 100755 --- a/bin/scripts/install_pr.sh +++ b/bin/scripts/install_pr.sh @@ -12,6 +12,8 @@ hasPython3=$? type python > /dev/null 2> /dev/null hasPython2=$? +BASE_PATH='.'; + if [[ "${hasWget}" -ne 0 ]] && [[ "${hasCurl}" -ne 0 ]]; then echo '---> Critical Error: wget or curl required to download pull request artifacts' exit 1 @@ -171,20 +173,20 @@ fi # Download packed PR artifact echo "---> Downloading the Pull Request #$pr_number" if [ $hasCurl -eq 0 ]; then - curl -skH "Authorization: token ${PR_TOKEN}" -o $HOME/temp.zip -L --get $archive_download_url + curl -skH "Authorization: token ${PR_TOKEN}" -o $BASE_PATH/temp.zip -L --get $archive_download_url elif [ $hasWget -eq 0 ]; then echo "wget" - wget --quiet --header="Authorization: token ${PR_TOKEN}" -O $HOME/temp.zip $archive_download_url + wget --quiet --header="Authorization: token ${PR_TOKEN}" -O $BASE_PATH/temp.zip $archive_download_url fi # Create new folder & extract PR artifact echo "---> Extracting packed Artifact" -mkdir -p $HOME/hyperion_pr$pr_number -unzip -p $HOME/temp.zip | tar --strip-components=2 -C $HOME/hyperion_pr$pr_number share/hyperion/ -xz +mkdir -p $BASE_PATH/hyperion_pr$pr_number +unzip -p $BASE_PATH/temp.zip | tar --strip-components=2 -C $BASE_PATH/hyperion_pr$pr_number share/hyperion/ -xz # Delete PR artifact echo '---> Remove temporary files' -rm $HOME/temp.zip 2>/dev/null +rm $BASE_PATH/temp.zip 2>/dev/null # Create the startup script echo '---> Create startup script' @@ -209,7 +211,7 @@ if [[ ! -z ${CURRENT_SERVICE} ]]; then fi fi'"" -TARGET_CONFIGDIR="$HOME/hyperion_pr$pr_number/config" +TARGET_CONFIGDIR="$BASE_PATH/config" if [[ ! -z ${CONFIGDIR} ]]; then STARTUP_SCRIPT+=" @@ -221,19 +223,19 @@ fi STARTUP_SCRIPT+=" # Start PR artifact -cd $HOME/hyperion_pr$pr_number +cd $BASE_PATH/hyperion_pr$pr_number ./bin/hyperiond -d -u $TARGET_CONFIGDIR" # Place startup script -echo "$STARTUP_SCRIPT" > $HOME/hyperion_pr$pr_number/$pr_number.sh +echo "$STARTUP_SCRIPT" > $BASE_PATH/hyperion_pr$pr_number/$pr_number.sh # Set the executen bit -chmod +x -R $HOME/hyperion_pr$pr_number/$pr_number.sh +chmod +x -R $BASE_PATH/hyperion_pr$pr_number/$pr_number.sh echo "*******************************************************************************" echo "Download finished!" $REBOOTMESSAGE -echo "You can test the pull request with this command: ~/hyperion_pr$pr_number/$pr_number.sh" -echo "Remove the test installation with: rm -R ~/hyperion_pr$pr_number" +echo "You can test the pull request with this command: $BASE_PATH/hyperion_pr$pr_number/$pr_number.sh" +echo "Remove the test installation with: rm -R $BASE_PATH/hyperion_pr$pr_number" echo "Feedback is welcome at https://github.com/hyperion-project/hyperion.ng/pull/$pr_number" echo "*******************************************************************************" diff --git a/effects/matrix.json b/effects/matrix.json index 2a2f295c..55c42c24 100644 --- a/effects/matrix.json +++ b/effects/matrix.json @@ -11,6 +11,6 @@ "grayscale": false, "imageSource": "file", "reverse": false, - "file": "matrix.gif" + "file": ":matrix.gif" } } diff --git a/libsrc/grabber/video/v4l2/V4L2Grabber.cpp b/libsrc/grabber/video/v4l2/V4L2Grabber.cpp index a0caa19d..8f504464 100644 --- a/libsrc/grabber/video/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/video/v4l2/V4L2Grabber.cpp @@ -1290,11 +1290,11 @@ QJsonArray V4L2Grabber::discover(const QJsonObject& params) format["format"] = pixelFormatToString(encodingFormat); QMap, QSet> combined = QMap, QSet>(); - for (auto enc : input.value().encodingFormats.values(encodingFormat)) + for (const auto &enc : input.value().encodingFormats.values(encodingFormat)) { std::pair width_height{enc.width, enc.height}; auto &com = combined[width_height]; - for (auto framerate : qAsConst(enc.framerates)) + for (auto framerate : enc.framerates) { com.insert(framerate); } @@ -1326,7 +1326,7 @@ QJsonArray V4L2Grabber::discover(const QJsonObject& params) device["video_inputs"] = video_inputs; QJsonObject controls, controls_default; - for (const auto &control : qAsConst(_deviceControls[device_property.key()])) + for (const auto &control : std::as_const(_deviceControls[device_property.key()])) { QJsonObject property; property["minValue"] = control.minValue; diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp index d4d75e9f..14f9c39a 100644 --- a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp @@ -446,11 +446,11 @@ bool LedDevicePhilipsHueBridge::checkApiError(const QJsonDocument &response, boo QJsonObject obj = response.object(); if (obj.contains(API_ERRORS)) { - QJsonArray errorList = obj.value(API_ERRORS).toArray(); + const QJsonArray errorList = obj.value(API_ERRORS).toArray(); if (!errorList.isEmpty()) { QStringList errors; - for (const QJsonValue &error : qAsConst(errorList)) + for (const QJsonValue &error : errorList) { QString errorString = error.toObject()[API_ERROR_DESCRIPTION].toString(); if (!errorString.contains("may not have effect")) @@ -909,9 +909,9 @@ void LedDevicePhilipsHueBridge::setDevicesMap(const QJsonDocument &doc) if (_useApiV2) { - QJsonArray devices = doc.array(); + const QJsonArray devices = doc.array(); - for (const QJsonValue &device : qAsConst(devices)) + for (const QJsonValue &device : devices) { QString deviceId = device.toObject().value("id").toString(); _devicesMap.insert(deviceId, device.toObject()); @@ -925,9 +925,9 @@ void LedDevicePhilipsHueBridge::setLightsMap(const QJsonDocument &doc) if (_useApiV2) { - QJsonArray lights = doc.array(); + const QJsonArray lights = doc.array(); - for (const QJsonValue &light : qAsConst(lights)) + for (const QJsonValue &light : lights) { QString lightId = light.toObject().value("id").toString(); _lightsMap.insert(lightId, light.toObject()); @@ -965,9 +965,9 @@ void LedDevicePhilipsHueBridge::setGroupMap(const QJsonDocument &doc) _groupsMap.clear(); if (_useApiV2) { - QJsonArray groups = doc.array(); + const QJsonArray groups = doc.array(); - for (const QJsonValue &group : qAsConst(groups)) + for (const QJsonValue &group : groups) { QString groupId = group.toObject().value("id").toString(); _groupsMap.insert(groupId, group.toObject()); @@ -995,9 +995,9 @@ void LedDevicePhilipsHueBridge::setEntertainmentSrvMap(const QJsonDocument &doc) if (_useApiV2) { - QJsonArray entertainmentSrvs = doc.array(); + const QJsonArray entertainmentSrvs = doc.array(); - for (const QJsonValue &entertainmentSrv : qAsConst(entertainmentSrvs)) + for (const QJsonValue &entertainmentSrv : entertainmentSrvs) { QString entertainmentSrvId = entertainmentSrv.toObject().value("id").toString(); _entertainmentMap.insert(entertainmentSrvId, entertainmentSrv.toObject()); @@ -1093,8 +1093,8 @@ QStringList LedDevicePhilipsHueBridge::getGroupLights(const QString& groupId) co { if (_useApiV2) { - QJsonArray lightServices = group.value( API_LIGHT_SERVICES ).toArray(); - for (const QJsonValue &light : qAsConst(lightServices)) + const QJsonArray lightServices = group.value( API_LIGHT_SERVICES ).toArray(); + for (const QJsonValue &light : lightServices) { groupLights.append( light.toObject().value(API_RID).toString()); } @@ -1140,7 +1140,7 @@ QJsonObject LedDevicePhilipsHueBridge::getEntertainmentSrvDetails(const QString& DebugIf( verbose, _log, "getEntertainmentSrvDetails [%s]", QSTRING_CSTR(deviceId) ); QJsonObject details; - for (const QJsonObject& entertainmentSrv : qAsConst(_entertainmentMap)) + for (const QJsonObject& entertainmentSrv : std::as_const(_entertainmentMap)) { QJsonObject owner = entertainmentSrv[API_OWNER].toObject(); @@ -1933,7 +1933,7 @@ bool LedDevicePhilipsHue::updateLights(const QMap &map) if(!_lightIds.empty()) { _lights.reserve(static_cast(_lightIds.size())); - for(const auto &id : qAsConst(_lightIds)) + for(const auto &id : std::as_const(_lightIds)) { if (map.contains(id)) { From b73e9f4996b81008613251092def2a6579fa1660 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Sun, 29 Oct 2023 21:12:59 +0100 Subject: [PATCH 58/98] Qt 6.7 (#1650) * Do not validate values for options without value * Clean-up * ws281x include files workaround * Revert "ws281x include files workaround" This reverts commit 1b983087183e3c563a191edd34a318cfd0cdace1. * Use https://github.com/hyperion-project/rpi_ws281x while fix is applied in original repository --- .gitmodules | 4 ++-- CMakeLists.txt | 11 +++++------ libsrc/commandline/Parser.cpp | 25 ++++++++++++++----------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/.gitmodules b/.gitmodules index 624c175c..2fd17349 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "dependencies/external/rpi_ws281x"] path = dependencies/external/rpi_ws281x - url = https://github.com/jgarff/rpi_ws281x - branch = master + url = https://github.com/hyperion-project/rpi_ws281x + branch = main [submodule "dependencies/external/flatbuffers"] path = dependencies/external/flatbuffers url = https://github.com/google/flatbuffers diff --git a/CMakeLists.txt b/CMakeLists.txt index 813cb750..32bbb7d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,10 +222,11 @@ if (HYPERION_LIGHT) SET ( DEFAULT_OSX OFF ) SET ( DEFAULT_QT OFF ) SET ( DEFAULT_V4L2 OFF ) - SET ( DEFAULT_AUDIO OFF ) SET ( DEFAULT_X11 OFF ) SET ( DEFAULT_XCB OFF ) + SET ( DEFAULT_AUDIO OFF ) + # Disable Input Servers SET ( DEFAULT_BOBLIGHT_SERVER OFF ) SET ( DEFAULT_CEC OFF ) @@ -274,15 +275,13 @@ message(STATUS "ENABLE_V4L2 = ${ENABLE_V4L2}") option(ENABLE_X11 "Enable the X11 grabber" ${DEFAULT_X11}) message(STATUS "ENABLE_X11 = ${ENABLE_X11}") -option(ENABLE_AUDIO "Enable the AUDIO grabber" ${DEFAULT_AUDIO}) -message(STATUS "ENABLE_AUDIO = ${ENABLE_AUDIO}") - -option(ENABLE_WS281XPWM "Enable the WS281x-PWM device" ${DEFAULT_WS281XPWM} ) -message(STATUS "ENABLE_WS281XPWM = ${ENABLE_WS281XPWM}") option(ENABLE_XCB "Enable the XCB grabber" ${DEFAULT_XCB}) message(STATUS "ENABLE_XCB = ${ENABLE_XCB}") +option(ENABLE_AUDIO "Enable the AUDIO grabber" ${DEFAULT_AUDIO}) +message(STATUS "ENABLE_AUDIO = ${ENABLE_AUDIO}") + removeIndent() message(STATUS "Input options:") diff --git a/libsrc/commandline/Parser.cpp b/libsrc/commandline/Parser.cpp index 0bceb682..df58fe1f 100644 --- a/libsrc/commandline/Parser.cpp +++ b/libsrc/commandline/Parser.cpp @@ -14,19 +14,22 @@ bool Parser::parse(const QStringList &arguments) return false; } - for(Option * option : _options) + for(Option * option : std::as_const(_options)) { - QString value = this->value(*option); - if (!option->validate(*this, value)) { - const QString error = option->getError(); - if (!error.isEmpty()) { - _errorText = tr("\"%1\" is not a valid option for %2, %3").arg(value, option->name(), error); + if (!option->valueName().isEmpty()) + { + QString value = this->value(*option); + if (!option->validate(*this, value)) { + const QString error = option->getError(); + if (!error.isEmpty()) { + _errorText = tr("\"%1\" is not a valid option for %2, %3").arg(value, option->name(), error); + } + else + { + _errorText = tr("\"%1\" is not a valid option for %2").arg(value, option->name()); + } + return false; } - else - { - _errorText = tr("\"%1\" is not a valid option for %2").arg(value, option->name()); - } - return false; } } return true; From 27027b224cc8f4db4ad3f30b1af0afec37eb2adf Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Sun, 29 Oct 2023 21:13:34 +0100 Subject: [PATCH 59/98] Fix self-signed certificate handling (#1649) --- CHANGELOG.md | 1 + .../leddevice/dev_net/LedDevicePhilipsHue.cpp | 6 +- libsrc/leddevice/dev_net/ProviderRestApi.cpp | 73 ++++++++++++++++++- libsrc/leddevice/dev_net/ProviderRestApi.h | 2 + 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 061f7b09..e4024253 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ Note: The wizard will configure an APIv2 capable bridge always with Entertainmen - Changed default build from Stretch to Buster - Support Qt 6.7, Update to Protobuf 23.4.0, Update mbedTLS to v3.4.0, Update flatbuffers to v23.5.26 - Use C++17 standard as default +- Added Pull Request (PR) installation script, allowing users to test development builds savely on Linux - Fixed missing include limits in QJsonSchemaChecker - Thanks @Portisch - Fixed dependencies for deb packages in Debian Bookworm (#1579) - Thanks @hg42, @Psirus - Fixed git version identification when run in docker and local code diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp index 14f9c39a..77712594 100644 --- a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp @@ -584,11 +584,7 @@ int LedDevicePhilipsHueBridge::close() bool LedDevicePhilipsHueBridge::configureSsl() { _restApi->setAlternateServerIdentity(_deviceBridgeId); - - if (_isDiyHue) - { - _restApi->acceptSelfSignedCertificates(true); - } + _restApi->acceptSelfSignedCertificates(true); bool success = _restApi->setCaCertificate(API_SSL_CA_CERTIFICATE_RESSOURCE); if (!success) diff --git a/libsrc/leddevice/dev_net/ProviderRestApi.cpp b/libsrc/leddevice/dev_net/ProviderRestApi.cpp index e2d07475..7321810f 100644 --- a/libsrc/leddevice/dev_net/ProviderRestApi.cpp +++ b/libsrc/leddevice/dev_net/ProviderRestApi.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include @@ -451,6 +453,63 @@ bool ProviderRestApi::checkServerIdentity(const QSslConfiguration& sslConfig) co return isServerIdentified; } +bool ProviderRestApi::matchesPinnedCertificate(const QSslCertificate& certificate) +{ + bool isMatching {false}; + + QList certificateInfos = certificate.subjectInfo(QSslCertificate::CommonName); + + if (certificateInfos.isEmpty()) + { + return false; + } + QString identifier = certificateInfos.constFirst(); + + QString appDataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + QString certDir = appDataDir + "/certificates"; + QDir().mkpath(certDir); + + QString filePath(certDir + "/" + identifier + ".pem"); + QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) + { + QList certificates = QSslCertificate::fromDevice(&file, QSsl::Pem); + if (!certificates.isEmpty()) + { + Debug (_log,"First used certificate loaded successfully"); + QSslCertificate pinnedeCertificate = certificates.constFirst(); + if (pinnedeCertificate == certificate) + { + isMatching = true; + } + } + else + { + Debug (_log,"Error reading first used certificate file: %s", QSTRING_CSTR(filePath)); + } + file.close(); + } + else + { + if (file.open(QIODevice::WriteOnly)) + { + QByteArray pemData = certificate.toPem(); + qint64 bytesWritten = file.write(pemData); + if (bytesWritten == pemData.size()) + { + Debug (_log,"First used certificate saved to file: %s", QSTRING_CSTR(filePath)); + isMatching = true; + } + else + { + Debug (_log,"Error writing first used certificate file: %s", QSTRING_CSTR(filePath)); + } + file.close(); + } + } + return isMatching; +} + void ProviderRestApi::onSslErrors(QNetworkReply* reply, const QList& errors) { int ignoredErrorCount {0}; @@ -466,11 +525,21 @@ void ProviderRestApi::onSslErrors(QNetworkReply* reply, const QList& } break; case QSslError::SelfSignedCertificate : - if (_isSeflSignedCertificateAccpeted) + if (_isSeflSignedCertificateAccpeted) + { + // Get the peer certificate associated with the error + QSslCertificate certificate = error.certificate(); + if (matchesPinnedCertificate(certificate)) { + Debug (_log,"'Trust on first use' - Certificate received matches pinned certificate"); ignoreSslError = true; } - break; + else + { + Error (_log,"'Trust on first use' - Certificate received does not match pinned certificate"); + } + } + break; default: break; } diff --git a/libsrc/leddevice/dev_net/ProviderRestApi.h b/libsrc/leddevice/dev_net/ProviderRestApi.h index b93d13ea..db4f9bd7 100644 --- a/libsrc/leddevice/dev_net/ProviderRestApi.h +++ b/libsrc/leddevice/dev_net/ProviderRestApi.h @@ -444,6 +444,8 @@ private: bool checkServerIdentity(const QSslConfiguration& sslConfig) const; + bool matchesPinnedCertificate(const QSslCertificate& certificate); + Logger* _log; /// QNetworkAccessManager object for sending REST-requests. From 8a54eff6563f5b47c7dd5dcef1fb2f41507e8cda Mon Sep 17 00:00:00 2001 From: Hyperion-Bot <20935312+Hyperion-Bot@users.noreply.github.com> Date: Mon, 30 Oct 2023 00:21:59 +0000 Subject: [PATCH 60/98] Update submodule rpi_ws281x --- dependencies/external/rpi_ws281x | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/external/rpi_ws281x b/dependencies/external/rpi_ws281x index 1f47b59e..49086d39 160000 --- a/dependencies/external/rpi_ws281x +++ b/dependencies/external/rpi_ws281x @@ -1 +1 @@ -Subproject commit 1f47b59ed603223d1376d36c788c89af67ae2fdc +Subproject commit 49086d3913367d2fb014a615f9d958a47867bc39 From f57c4f84acd92dede4a2e5e1c8eef6786eaff99c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 14:38:23 +0100 Subject: [PATCH 61/98] Bump jurplel/install-qt-action from 3 to 4 (#1652) Bumps [jurplel/install-qt-action](https://github.com/jurplel/install-qt-action) from 3 to 4. --- .github/workflows/pull-request.yml | 2 +- .github/workflows/push-master.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index eee7c150..12d69ae4 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -137,7 +137,7 @@ jobs: echo -n "+PR${{ github.event.pull_request.number }}" >> .version - name: Install Qt - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: version: ${{env.QT_VERSION}} target: 'desktop' diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index b0ee7a1d..23c0abc8 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -102,7 +102,7 @@ jobs: submodules: recursive - name: Install Qt - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: version: ${{env.QT_VERSION}} target: 'desktop' From c9518db59742ddae6da7c93a49d3eaf3fe5d7ab5 Mon Sep 17 00:00:00 2001 From: LordGrey Date: Fri, 3 Nov 2023 19:54:59 +0100 Subject: [PATCH 62/98] Revert "Bump jurplel/install-qt-action from 3 to 4 (#1652)" This reverts commit f57c4f84acd92dede4a2e5e1c8eef6786eaff99c. --- .github/workflows/pull-request.yml | 2 +- .github/workflows/push-master.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 12d69ae4..eee7c150 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -137,7 +137,7 @@ jobs: echo -n "+PR${{ github.event.pull_request.number }}" >> .version - name: Install Qt - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@v3 with: version: ${{env.QT_VERSION}} target: 'desktop' diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index 23c0abc8..b0ee7a1d 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -102,7 +102,7 @@ jobs: submodules: recursive - name: Install Qt - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@v3 with: version: ${{env.QT_VERSION}} target: 'desktop' From 91270966f9c7012f8eafca6bd1825e985e4c9501 Mon Sep 17 00:00:00 2001 From: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com> Date: Thu, 16 Nov 2023 21:05:56 +0100 Subject: [PATCH 63/98] Refactor/Create APT/DNF Repository (#1648) --- .ci/ci_build.sh | 71 --- .ci/ci_install.sh | 40 -- .devcontainer.json | 37 +- .github/scripts/build.sh | 59 +++ .github/workflows/apt.yml | 154 ------- .github/workflows/apt/amd64.json | 58 --- .github/workflows/apt/arm64.json | 51 --- .github/workflows/apt/armhf.json | 51 --- .github/workflows/cleanup.yml | 4 +- .github/workflows/codeql.yml | 25 +- .github/workflows/nightly.yml | 187 -------- .github/workflows/pull-request.yml | 193 -------- .github/workflows/push-master.yml | 201 --------- .github/workflows/push_pull.yml | 49 ++ .github/workflows/qt5_6.yml | 250 +++++++++++ .github/workflows/release.yml | 9 +- CMakeLists.txt | 423 +++++++++--------- bin/scripts/docker-compile.sh | 153 +++++-- bin/scripts/install_pr.sh | 12 +- bin/service/hyperion.systemd | 2 +- bin/service/hyperion.xml | 22 + cmake/Dependencies.cmake | 51 ++- cmake/Findqmdnsengine.cmake | 8 +- cmake/LDGold.cmake | 17 - .../{hyperiond.desktop => hyperion.desktop} | 4 +- cmake/desktop/hyperion.metainfo.xml | 48 ++ cmake/desktop/hyperiond_128.png | Bin 16979 -> 0 bytes cmake/package-scripts/postinst | 8 +- cmake/package-scripts/prerm | 28 +- cmake/packages.cmake | 2 +- cmake/win/win_rc.cmake | 15 - debian/control.in | 12 - debian/distributions | 58 --- debian/rules.in | 32 -- dependencies/CMakeLists-qmdnsengine.txt.in | 26 -- dependencies/CMakeLists.txt | 86 ++-- dependencies/external/mbedtls | 2 +- include/grabber/OsxFrameGrabberMock.h | 93 ---- .../grabber/{ => amlogic}/AmlogicGrabber.h | 2 +- .../grabber/{ => amlogic}/AmlogicWrapper.h | 2 +- include/grabber/{ => audio}/AudioGrabber.h | 0 .../grabber/{ => audio}/AudioGrabberLinux.h | 31 +- .../grabber/{ => audio}/AudioGrabberWindows.h | 4 +- include/grabber/{ => audio}/AudioWrapper.h | 12 +- .../grabber/{ => directx}/DirectXGrabber.h | 0 .../grabber/{ => directx}/DirectXWrapper.h | 2 +- .../{ => dispmanx}/DispmanxFrameGrabber.h | 0 .../grabber/{ => dispmanx}/DispmanxWrapper.h | 2 +- .../FramebufferFrameGrabber.h | 0 .../{ => framebuffer}/FramebufferWrapper.h | 2 +- include/grabber/{ => osx}/OsxFrameGrabber.h | 4 - include/grabber/{ => osx}/OsxWrapper.h | 2 +- include/grabber/{ => qt}/QtGrabber.h | 0 include/grabber/{ => qt}/QtWrapper.h | 2 +- include/grabber/{ => video}/EncoderThread.h | 12 +- include/grabber/{ => video}/VideoWrapper.h | 4 +- .../{ => video/mediafoundation}/MFGrabber.h | 2 +- .../grabber/{ => video/v4l2}/V4L2Grabber.h | 2 +- include/grabber/{ => x11}/X11Grabber.h | 0 include/grabber/{ => x11}/X11Wrapper.h | 2 +- include/grabber/{ => xcb}/XcbGrabber.h | 0 include/grabber/{ => xcb}/XcbWrapper.h | 2 +- include/hyperion/ImageToLedsMap.h | 2 +- include/utils/Process.h | 9 +- libsrc/CMakeLists.txt | 6 +- libsrc/api/CMakeLists.txt | 32 +- libsrc/api/JsonAPI.cpp | 26 +- libsrc/blackborder/CMakeLists.txt | 14 +- libsrc/boblightserver/CMakeLists.txt | 15 +- libsrc/cec/CMakeLists.txt | 23 +- libsrc/commandline/CMakeLists.txt | 31 +- libsrc/db/CMakeLists.txt | 14 +- libsrc/effectengine/CMakeLists.txt | 57 +-- libsrc/effectengine/EffectEngine.qrc.in | 2 +- libsrc/flatbufserver/CMakeLists.txt | 97 ++-- libsrc/forwarder/CMakeLists.txt | 17 +- libsrc/grabber/CMakeLists.txt | 22 +- libsrc/grabber/amlogic/AmlogicGrabber.cpp | 2 +- libsrc/grabber/amlogic/AmlogicWrapper.cpp | 2 +- libsrc/grabber/amlogic/CMakeLists.txt | 22 +- libsrc/grabber/audio/AudioGrabber.cpp | 2 +- libsrc/grabber/audio/AudioGrabberLinux.cpp | 61 ++- libsrc/grabber/audio/AudioGrabberWindows.cpp | 28 +- libsrc/grabber/audio/AudioWrapper.cpp | 2 +- libsrc/grabber/audio/CMakeLists.txt | 61 +-- libsrc/grabber/directx/CMakeLists.txt | 23 +- libsrc/grabber/directx/DirectXGrabber.cpp | 2 +- libsrc/grabber/directx/DirectXWrapper.cpp | 2 +- libsrc/grabber/dispmanx/CMakeLists.txt | 25 +- .../grabber/dispmanx/DispmanxFrameGrabber.cpp | 2 +- libsrc/grabber/dispmanx/DispmanxWrapper.cpp | 2 +- libsrc/grabber/framebuffer/CMakeLists.txt | 15 +- .../framebuffer/FramebufferFrameGrabber.cpp | 2 +- .../framebuffer/FramebufferWrapper.cpp | 2 +- libsrc/grabber/osx/CMakeLists.txt | 15 +- libsrc/grabber/osx/OsxFrameGrabber.cpp | 2 +- libsrc/grabber/osx/OsxFrameGrabberMock.cpp | 159 ------- libsrc/grabber/osx/OsxWrapper.cpp | 2 +- libsrc/grabber/qt/CMakeLists.txt | 15 +- libsrc/grabber/qt/QtGrabber.cpp | 4 +- libsrc/grabber/qt/QtWrapper.cpp | 2 +- libsrc/grabber/video/CMakeLists.txt | 51 ++- libsrc/grabber/video/EncoderThread.cpp | 2 +- libsrc/grabber/video/VideoWrapper.cpp | 2 +- .../video/mediafoundation/MFGrabber.cpp | 4 +- .../video/mediafoundation/MFSourceReaderCB.h | 2 +- libsrc/grabber/video/v4l2/V4L2Grabber.cpp | 2 +- libsrc/grabber/x11/CMakeLists.txt | 29 +- libsrc/grabber/x11/X11Grabber.cpp | 2 +- libsrc/grabber/x11/X11Wrapper.cpp | 2 +- libsrc/grabber/xcb/CMakeLists.txt | 26 +- libsrc/grabber/xcb/XcbGrabber.cpp | 2 +- libsrc/grabber/xcb/XcbWrapper.cpp | 2 +- libsrc/hyperion/CMakeLists.txt | 64 ++- libsrc/jsonserver/CMakeLists.txt | 17 +- libsrc/leddevice/CMakeLists.txt | 89 ++-- libsrc/mdns/CMakeLists.txt | 25 +- libsrc/protoserver/CMakeLists.txt | 49 +- libsrc/python/CMakeLists.txt | 48 +- libsrc/ssdp/CMakeLists.txt | 15 +- libsrc/utils/CMakeLists.txt | 114 +++-- libsrc/utils/Process.cpp | 145 +++--- libsrc/webserver/CMakeLists.txt | 44 +- resources/CMakeLists.txt | 14 +- resources/icons/hyperion-128px.png | Bin 0 -> 10834 bytes resources/icons/hyperion-16px.png | Bin 0 -> 717 bytes resources/icons/hyperion-192px.png | Bin 0 -> 21900 bytes resources/icons/hyperion-22px.png | Bin 0 -> 1728 bytes resources/icons/hyperion-24px.png | Bin 0 -> 1894 bytes resources/icons/hyperion-256px.png | Bin 0 -> 31694 bytes resources/icons/hyperion-32px.png | Bin 0 -> 1459 bytes resources/icons/hyperion-36px.png | Bin 0 -> 2462 bytes resources/icons/hyperion-48px.png | Bin 0 -> 2556 bytes resources/icons/hyperion-512px.png | Bin 0 -> 86035 bytes resources/icons/hyperion-64px.png | Bin 0 -> 3845 bytes resources/icons/hyperion-72px.png | Bin 0 -> 4451 bytes resources/icons/hyperion-96px.png | Bin 0 -> 7921 bytes resources/icons/hyperion-icon-32px.png | Bin 1454 -> 0 bytes resources/icons/hyperion-icon-512px.png | Bin 69597 -> 0 bytes snap/snapcraft.yaml | 4 +- src/CMakeLists.txt | 6 +- src/hyperion-aml/AmlogicWrapper.h | 2 +- src/hyperion-aml/CMakeLists.txt | 41 +- src/hyperion-dispmanx/CMakeLists.txt | 35 +- src/hyperion-dispmanx/DispmanxWrapper.h | 4 +- src/hyperion-framebuffer/CMakeLists.txt | 42 +- src/hyperion-framebuffer/FramebufferWrapper.h | 2 +- src/hyperion-osx/CMakeLists.txt | 30 +- src/hyperion-osx/OsxWrapper.h | 2 +- src/hyperion-qt/CMakeLists.txt | 51 +-- src/hyperion-qt/QtWrapper.h | 2 +- src/hyperion-remote/CMakeLists.txt | 58 +-- src/hyperion-v4l2/CMakeLists.txt | 41 +- src/hyperion-v4l2/hyperion-v4l2.cpp | 4 +- src/hyperion-x11/CMakeLists.txt | 42 +- src/hyperion-x11/X11Wrapper.h | 2 +- src/hyperion-xcb/CMakeLists.txt | 29 +- src/hyperion-xcb/XcbWrapper.h | 2 +- src/hyperiond/CMakeLists.txt | 194 ++++---- src/hyperiond/hyperiond.h | 22 +- src/hyperiond/main.cpp | 2 +- src/hyperiond/systray.cpp | 4 +- test/CMakeLists.txt | 14 +- test/dispmanx2png/CMakeLists.txt | 4 +- test/dispmanx2png/dispmanx2png.cpp | 2 +- 165 files changed, 1918 insertions(+), 2924 deletions(-) delete mode 100755 .ci/ci_build.sh delete mode 100755 .ci/ci_install.sh create mode 100755 .github/scripts/build.sh delete mode 100644 .github/workflows/apt.yml delete mode 100644 .github/workflows/apt/amd64.json delete mode 100644 .github/workflows/apt/arm64.json delete mode 100644 .github/workflows/apt/armhf.json delete mode 100644 .github/workflows/nightly.yml delete mode 100644 .github/workflows/pull-request.yml delete mode 100644 .github/workflows/push-master.yml create mode 100644 .github/workflows/push_pull.yml create mode 100644 .github/workflows/qt5_6.yml create mode 100644 bin/service/hyperion.xml delete mode 100644 cmake/LDGold.cmake rename cmake/desktop/{hyperiond.desktop => hyperion.desktop} (61%) create mode 100644 cmake/desktop/hyperion.metainfo.xml delete mode 100644 cmake/desktop/hyperiond_128.png delete mode 100644 cmake/win/win_rc.cmake delete mode 100644 debian/control.in delete mode 100644 debian/distributions delete mode 100644 debian/rules.in delete mode 100644 dependencies/CMakeLists-qmdnsengine.txt.in delete mode 100644 include/grabber/OsxFrameGrabberMock.h rename include/grabber/{ => amlogic}/AmlogicGrabber.h (97%) rename include/grabber/{ => amlogic}/AmlogicWrapper.h (95%) rename include/grabber/{ => audio}/AudioGrabber.h (100%) rename include/grabber/{ => audio}/AudioGrabberLinux.h (86%) rename include/grabber/{ => audio}/AudioGrabberWindows.h (98%) rename include/grabber/{ => audio}/AudioWrapper.h (90%) rename include/grabber/{ => directx}/DirectXGrabber.h (100%) rename include/grabber/{ => directx}/DirectXWrapper.h (96%) rename include/grabber/{ => dispmanx}/DispmanxFrameGrabber.h (100%) rename include/grabber/{ => dispmanx}/DispmanxWrapper.h (95%) rename include/grabber/{ => framebuffer}/FramebufferFrameGrabber.h (100%) rename include/grabber/{ => framebuffer}/FramebufferWrapper.h (94%) rename include/grabber/{ => osx}/OsxFrameGrabber.h (95%) rename include/grabber/{ => osx}/OsxWrapper.h (95%) rename include/grabber/{ => qt}/QtGrabber.h (100%) rename include/grabber/{ => qt}/QtWrapper.h (97%) rename include/grabber/{ => video}/EncoderThread.h (91%) rename include/grabber/{ => video}/VideoWrapper.h (89%) rename include/grabber/{ => video/mediafoundation}/MFGrabber.h (98%) rename include/grabber/{ => video/v4l2}/V4L2Grabber.h (99%) rename include/grabber/{ => x11}/X11Grabber.h (100%) rename include/grabber/{ => x11}/X11Wrapper.h (97%) rename include/grabber/{ => xcb}/XcbGrabber.h (100%) rename include/grabber/{ => xcb}/XcbWrapper.h (94%) delete mode 100644 libsrc/grabber/osx/OsxFrameGrabberMock.cpp create mode 100644 resources/icons/hyperion-128px.png create mode 100644 resources/icons/hyperion-16px.png create mode 100644 resources/icons/hyperion-192px.png create mode 100644 resources/icons/hyperion-22px.png create mode 100644 resources/icons/hyperion-24px.png create mode 100644 resources/icons/hyperion-256px.png create mode 100644 resources/icons/hyperion-32px.png create mode 100644 resources/icons/hyperion-36px.png create mode 100644 resources/icons/hyperion-48px.png create mode 100644 resources/icons/hyperion-512px.png create mode 100644 resources/icons/hyperion-64px.png create mode 100644 resources/icons/hyperion-72px.png create mode 100644 resources/icons/hyperion-96px.png delete mode 100644 resources/icons/hyperion-icon-32px.png delete mode 100644 resources/icons/hyperion-icon-512px.png diff --git a/.ci/ci_build.sh b/.ci/ci_build.sh deleted file mode 100755 index df276f00..00000000 --- a/.ci/ci_build.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -# detect CI -if [ "$HOME" != "" ]; then - # GitHub Actions - echo "Github Actions detected" - CI_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')" - CI_BUILD_DIR="$GITHUB_WORKSPACE" -else - # for executing in non ci environment - CI_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')" -fi - -# set environment variables if not exists -[ -z "${BUILD_TYPE}" ] && BUILD_TYPE="Debug" - -# Determine cmake build type; tag builds are Release, else Debug (-dev appends to platform) -if [[ $BUILD_SOURCEBRANCH == *"refs/tags"* || $GITHUB_REF == *"refs/tags"* ]]; then - BUILD_TYPE=Release -else - PLATFORM=${PLATFORM}-dev -fi - -echo "Platform: ${PLATFORM}, build type: ${BUILD_TYPE}, CI_NAME: $CI_NAME, docker image: ${DOCKER_IMAGE}, docker type: ${DOCKER_TAG}" - -# Build the package on osx or linux -if [[ "$CI_NAME" == 'osx' || "$CI_NAME" == 'darwin' ]]; then - echo "Compile Hyperion on OSX or Darwin" - # compile prepare - mkdir build || exit 1 - cd build - cmake -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=/usr/local ../ || exit 2 - make -j $(sysctl -n hw.ncpu) package || exit 3 - cd ${CI_BUILD_DIR} && source /${CI_BUILD_DIR}/test/testrunner.sh || exit 4 - exit 0; - exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; } -elif [[ $CI_NAME == *"mingw64_nt"* || "$CI_NAME" == 'windows_nt' ]]; then - echo "Compile Hyperion on Windows" - # compile prepare - echo "Number of Cores $NUMBER_OF_PROCESSORS" - mkdir build || exit 1 - cd build - cmake -G "Visual Studio 17 2022" -A x64 -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE="Release" ../ || exit 2 - cmake --build . --target package --config "Release" -- -nologo -v:m -maxcpucount || exit 3 - exit 0; - exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; } -elif [[ "$CI_NAME" == 'linux' ]]; then - echo "Compile Hyperion with DOCKER_IMAGE = ${DOCKER_IMAGE}, DOCKER_TAG = ${DOCKER_TAG} and friendly name DOCKER_NAME = ${DOCKER_NAME}" - # set GitHub Container Registry url - REGISTRY_URL="ghcr.io/hyperion-project/${DOCKER_IMAGE}" - # take ownership of deploy dir - mkdir ${CI_BUILD_DIR}/deploy - - # run docker - docker run --rm \ - -v "${CI_BUILD_DIR}/deploy:/deploy" \ - -v "${CI_BUILD_DIR}:/source:ro" \ - $REGISTRY_URL:$DOCKER_TAG \ - /bin/bash -c "mkdir hyperion && cp -r source/. /hyperion && - cd /hyperion && mkdir build && cd build && - cmake -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ../ || exit 2 && - make -j $(nproc) package || exit 3 && - cp /hyperion/build/bin/h* /deploy/ 2>/dev/null || : && - cp /hyperion/build/Hyperion-* /deploy/ 2>/dev/null || : && - cd /hyperion && source /hyperion/test/testrunner.sh || exit 4 && - exit 0; - exit 1 " || { echo "---> Hyperion compilation failed! Abort"; exit 5; } - - # overwrite file owner to current user - sudo chown -fR $(stat -c "%U:%G" ${CI_BUILD_DIR}/deploy) ${CI_BUILD_DIR}/deploy -fi diff --git a/.ci/ci_install.sh b/.ci/ci_install.sh deleted file mode 100755 index 86c6ffd4..00000000 --- a/.ci/ci_install.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -# detect CI -if [ "$HOME" != "" ]; then - # GitHub Actions - CI_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')" - CI_BUILD_DIR="$GITHUB_WORKSPACE" -else - # for executing in non ci environment - CI_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')" -fi - -function installAndUpgrade() -{ - arr=("$@") - for i in "${arr[@]}"; - do - list_output=`brew list --formula | grep $i` - outdated_output=`brew outdated | grep $i` - - if [[ ! -z "$list_output" ]]; then - if [[ ! -z "$outdated_output" ]]; then - echo "Outdated package: ${outdated_output}" - brew unlink ${outdated_output} - brew upgrade $i - brew link --overwrite $i - fi - else - brew install $i - fi - done -} - -# install osx deps for hyperion compile -if [[ $CI_NAME == 'osx' || $CI_NAME == 'darwin' ]]; then - echo "Install dependencies" - brew update - dependencies=("qt5" "python" "libusb" "cmake" "doxygen") - installAndUpgrade "${dependencies[@]}" -fi diff --git a/.devcontainer.json b/.devcontainer.json index 4cce9be7..966a658b 100644 --- a/.devcontainer.json +++ b/.devcontainer.json @@ -1,20 +1,25 @@ { "name": "Hyperion.ng Linux", - "extensions": [ - "twxs.cmake", - "ms-vscode.cpptools", - "ms-vscode.cmake-tools", - "spmeesseman.vscode-taskexplorer", - "yzhang.markdown-all-in-one", - "CoenraadS.bracket-pair-colorizer", - "vscode-icons-team.vscode-icons", - "editorconfig.editorconfig" - ], - "settings": { - "editor.formatOnSave": false, - "cmake.environment": { - }, - }, + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + "extensions": [ + "twxs.cmake", + "ms-vscode.cpptools", + "ms-vscode.cmake-tools", + "spmeesseman.vscode-taskexplorer", + "yzhang.markdown-all-in-one", + "CoenraadS.bracket-pair-colorizer", + "vscode-icons-team.vscode-icons", + "editorconfig.editorconfig", + "RVSmartPorting.rpm-spec-ext" + ], + "settings": { + "editor.formatOnSave": false, + "cmake.environment": { } + } + } + }, "forwardPorts": [8090, 8092], - "postCreateCommand": "git submodule update --recursive --init && sudo apt-get update && sudo apt-get install -y git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libturbojpeg0-dev libssl-dev" + "postCreateCommand": "git submodule update --recursive --init && sudo apt-get update && sudo apt-get install -y git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libturbojpeg0-dev libssl-dev libasound2-dev" } diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh new file mode 100755 index 00000000..5880abb9 --- /dev/null +++ b/.github/scripts/build.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# set environment variables if not exists +[ -z "${BUILD_TYPE}" ] && BUILD_TYPE="Debug" +[ -z "${TARGET_ARCH}" ] && TARGET_ARCH="linux/amd64" +[ -z "${PLATFORM}" ] && PLATFORM="x11" + +# Determine cmake build type; tag builds are Release, else Debug (-dev appends to platform) +if [[ $GITHUB_REF == *"refs/tags"* ]]; then + BUILD_TYPE=Release +else + PLATFORM=${PLATFORM}-dev +fi + +echo "Compile Hyperion on '${RUNNER_OS}' with build type '${BUILD_TYPE}' and platform '${PLATFORM}'" + +# Build the package on MacOS, Windows or Linux +if [[ "$RUNNER_OS" == 'macOS' ]]; then + mkdir build || exit 1 + cd build + cmake -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=/usr/local ../ || exit 2 + make -j $(sysctl -n hw.ncpu) package || exit 3 + cd ${GITHUB_WORKSPACE} && source /${GITHUB_WORKSPACE}/test/testrunner.sh || exit 4 + exit 0; + exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; } +elif [[ $RUNNER_OS == "Windows" ]]; then + echo "Number of Cores $NUMBER_OF_PROCESSORS" + mkdir build || exit 1 + cd build + cmake -G "Visual Studio 17 2022" -A x64 -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE="Release" ../ || exit 2 + cmake --build . --target package --config "Release" -- -nologo -v:m -maxcpucount || exit 3 + exit 0; + exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; } +elif [[ "$RUNNER_OS" == 'Linux' ]]; then + echo "Docker arguments used: DOCKER_IMAGE=${DOCKER_IMAGE}, DOCKER_TAG=${DOCKER_TAG}, TARGET_ARCH=${TARGET_ARCH}" + # verification bypass of external dependencies + git config --global --add safe.directory "${GITHUB_WORKSPACE}/dependencies/external/*" + # set GitHub Container Registry url + REGISTRY_URL="ghcr.io/hyperion-project/${DOCKER_IMAGE}" + # take ownership of deploy dir + mkdir ${GITHUB_WORKSPACE}/deploy + + # run docker + docker run --rm --platform=${TARGET_ARCH} \ + -v "${GITHUB_WORKSPACE}/deploy:/deploy" \ + -v "${GITHUB_WORKSPACE}:/source:rw" \ + $REGISTRY_URL:$DOCKER_TAG \ + /bin/bash -c "mkdir -p /source/build && cd /source/build && + cmake -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ../ || exit 2 && + cmake --build /source/build --target package -- -j $(nproc) || exit 3 && + cp /source/build/bin/h* /deploy/ 2>/dev/null || : && + cp /source/build/Hyperion-* /deploy/ 2>/dev/null || : && + cd /source && source /source/test/testrunner.sh || exit 5 && + exit 0; + exit 1 " || { echo "---> Hyperion compilation failed! Abort"; exit 5; } + + # overwrite file owner to current user + sudo chown -fR $(stat -c "%U:%G" ${GITHUB_WORKSPACE}/deploy) ${GITHUB_WORKSPACE}/deploy +fi diff --git a/.github/workflows/apt.yml b/.github/workflows/apt.yml deleted file mode 100644 index 99bce214..00000000 --- a/.github/workflows/apt.yml +++ /dev/null @@ -1,154 +0,0 @@ -name: Hyperion APT Build -on: - workflow_call: - inputs: - head_sha: - type: string - description: The branch, tag or SHA to checkout - required: true - secrets: - APT_GPG: - required: true - APT_USER: - required: true - APT_PASSWORD: - required: true - APT_DRAFT: - required: true - workflow_dispatch: - inputs: - head_sha: - type: string - description: The branch, tag or SHA to checkout - required: true - secrets: - APT_GPG: - required: true - APT_USER: - required: true - APT_PASSWORD: - required: true - APT_DRAFT: - required: true - -jobs: - setup: - name: Setup APT build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Set APT matrix - id: apt-ppa - run: | - APT=$(jq -n '.include |= [ inputs[] | select(.["exclude"] != true)]' .github/workflows/apt/*.json --compact-output) - echo "apt=$APT" >> $GITHUB_OUTPUT - - outputs: - apt-matrix: ${{ steps.apt-ppa.outputs.apt }} - - build: - name: ${{ matrix.description }} - needs: [setup] - runs-on: ubuntu-latest - strategy: - matrix: ${{ fromJson(needs.setup.outputs.apt-matrix) }} - - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.inputs.head_sha || github.event.client_payload.head_sha }} - submodules: true - - - name: Generate environment variables - run: | - tr -d '\n' < .version > temp && mv temp .version - VERSION=$(cat .version) - echo VERSION=${VERSION} >> $GITHUB_ENV - if [[ $VERSION == *"-"* ]]; then - echo STANDARDS_VERSION=$(echo ${VERSION%-*}) >> $GITHUB_ENV - echo TARBALL_VERSION=$(echo ${VERSION%-*}) >> $GITHUB_ENV - echo DEBIAN_FORMAT='3.0 (quilt)' >> $GITHUB_ENV - else - echo STANDARDS_VERSION=$(echo ${VERSION%+*}) >> $GITHUB_ENV - echo TARBALL_VERSION=${VERSION}~$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV - echo DEBIAN_FORMAT='3.0 (native)' >> $GITHUB_ENV - fi - echo DISTRIBUTION=$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV - - - name: Build package - shell: bash - run: | - mkdir -p "${GITHUB_WORKSPACE}/deploy" - docker run --rm \ - -v "${GITHUB_WORKSPACE}/deploy:/deploy" \ - -v "${GITHUB_WORKSPACE}:/source:rw" \ - ghcr.io/hyperion-project/${{ matrix.architecture }}:${{ env.DISTRIBUTION }} \ - /bin/bash -c "cd /source && \ - mkdir -p debian/source && echo '${{ env.DEBIAN_FORMAT }}' > debian/source/format && \ - dch --create --distribution ${{ env.DISTRIBUTION }} --package 'hyperion' -v '${{ env.VERSION }}~${{ env.DISTRIBUTION }}' '${{ github.event.commits[0].message }}' && \ - cp -fr LICENSE debian/copyright && \ - sed 's/@BUILD_DEPENDS@/${{ matrix.build-depends }}/g; s/@DEPENDS@/${{ matrix.package-depends }}/g; s/@ARCHITECTURE@/${{ matrix.architecture }}/g; s/@STANDARDS_VERSION@/${{ env.STANDARDS_VERSION }}/g' debian/control.in > debian/control && \ - sed 's/@CMAKE_ENVIRONMENT@/${{ matrix.cmake-environment }}/g' debian/rules.in > debian/rules && \ - tar -cJf ../hyperion_${{ env.TARBALL_VERSION }}.orig.tar.xz . && \ - debuild --no-lintian -uc -us && \ - cp ../hyperion_*.deb /deploy" - - - name: Upload package artifact - if: ${{ startsWith(github.event.ref, 'refs/tags') || github.event_name == 'workflow_dispatch' }} - uses: actions/upload-artifact@v3 - with: - path: deploy - retention-days: 1 - - publish: - name: Publish APT packages - if: ${{ startsWith(github.event.ref, 'refs/tags') || github.event_name == 'workflow_dispatch' }} - needs: [setup, build] - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.event.inputs.head_sha || github.event.client_payload.head_sha }} - - - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v6.0.0 - with: - gpg_private_key: ${{ secrets.APT_GPG }} - - - name: Install reprepro - run: sudo apt -y install reprepro - - - name: Make build folders, export public GPG key and copy distributions file - run: | - mkdir -p apt/{conf,dists,db} - gpg --armor --output apt/hyperion.pub.key --export 'admin@hyperion-project.org' - cp debian/distributions apt/conf/distributions - - - name: Create initial structure/packages files and symbolic links - run: | - reprepro -Vb apt createsymlinks - reprepro -Vb apt export - - - name: Download artifacts - uses: actions/download-artifact@v3.0.2 - - - name: Include artifacts into the package source - run: | - for file in artifact/hyperion_*.deb; do - if [ -f "$file" ]; then - dist=${file#*~} - dist=${dist%_*} - reprepro -Vb apt/ includedeb "$dist" "$file" - fi - done - - - name: Upload packages to APT server (DRAFT) - uses: SamKirkland/FTP-Deploy-Action@v4.3.4 - with: - server: apt.hyperion-project.org - username: ${{ secrets.APT_USER }} - password: ${{ secrets.APT_PASSWORD }} - local-dir: "./apt/" - server-dir: ${{ secrets.APT_DRAFT }} - dangerous-clean-slate: true diff --git a/.github/workflows/apt/amd64.json b/.github/workflows/apt/amd64.json deleted file mode 100644 index ecfa56c3..00000000 --- a/.github/workflows/apt/amd64.json +++ /dev/null @@ -1,58 +0,0 @@ -[ - { - "distribution": "Focal", - "architecture": "amd64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.8, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg, libcec4", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 20.04 (Focal Fossa) (amd64)" - }, - { - "distribution": "Jammy", - "architecture": "amd64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 22.04 (Jammy Jellyfish) (amd64)" - }, - { - "distribution": "Kinetic", - "architecture": "amd64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 22.10 (Kinetic Kudu) (amd64)" - }, - { - "distribution": "Lunar", - "architecture": "amd64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.11, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 23.04 (Lunar Lobster) (amd64)" - }, - { - "distribution": "Buster", - "architecture": "amd64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.7, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec4", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Debian 10.x (Buster) (amd64)" - }, - { - "distribution": "Bullseye", - "architecture": "amd64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Debian 11.x (Bullseye) (amd64)" - }, - { - "distribution": "Bookworm", - "architecture": "amd64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.11, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg0, libcec6", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Debian 12.x (Bookworm) (amd64)" - } -] diff --git a/.github/workflows/apt/arm64.json b/.github/workflows/apt/arm64.json deleted file mode 100644 index 8cf6325e..00000000 --- a/.github/workflows/apt/arm64.json +++ /dev/null @@ -1,51 +0,0 @@ -[ - { - "distribution": "Focal", - "architecture": "arm64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.8, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg, libcec4", - "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 20.04 (Focal Fossa) (arm64)" - }, - { - "distribution": "Jammy", - "architecture": "arm64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", - "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 22.04 (Jammy Jellyfish) (arm64)" - }, - { - "distribution": "Kinetic", - "architecture": "arm64", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", - "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 22.10 (Kinetic Kudu) (arm64)" - }, - { - "distribution": "Buster", - "architecture": "arm64", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.7, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec4", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Debian 10.x (Buster) (arm64)" - }, - { - "distribution": "Bullseye", - "architecture": "arm64", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Debian 11.x (Bullseye) (arm64)" - }, - { - "distribution": "Bookworm", - "architecture": "arm64", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.11, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg0, libcec6", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Debian 12.x (Bookworm) (arm64)", - "exclude" : true - } -] diff --git a/.github/workflows/apt/armhf.json b/.github/workflows/apt/armhf.json deleted file mode 100644 index ed4b9b4d..00000000 --- a/.github/workflows/apt/armhf.json +++ /dev/null @@ -1,51 +0,0 @@ -[ - { - "distribution": "Focal", - "architecture": "armhf", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.8, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg, libcec4", - "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 20.04 (Focal Fossa) (armhf)" - }, - { - "distribution": "Jammy", - "architecture": "armhf", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", - "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 22.04 (Jammy Jellyfish) (armhf)" - }, - { - "distribution": "Kinetic", - "architecture": "armhf", - "build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev", - "package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6", - "cmake-environment": "-DENABLE_DISPMANX=OFF -DENABLE_X11=ON -DENABLE_XCB=ON -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Ubuntu 22.10 (Kinetic Kudu) (armhf)" - }, - { - "distribution": "Buster", - "architecture": "armhf", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl1.0-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.7, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec4", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Debian 10.x (Buster) (armhf)" - }, - { - "distribution": "Bullseye", - "architecture": "armhf", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Debian 11.x (Bullseye) (armhf)" - }, - { - "distribution": "Bookworm", - "architecture": "armhf", - "build-depends": "git, cmake, python3-dev, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, build-essential, libusb-1.0-0-dev, libcec-dev, libssl-dev, libraspberrypi-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libmbedtls-dev", - "package-depends": "libpython3.11, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg0, libcec6", - "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release", - "description": "Debian 12.x (Bookworm) (armhf)", - "exclude" : true - } -] diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml index 95ab9fd7..48c42fb0 100644 --- a/.github/workflows/cleanup.yml +++ b/.github/workflows/cleanup.yml @@ -1,4 +1,4 @@ -name: Clean artifacts +name: 🧹 Cleanup old artifacts # Run cleanup workflow at the end of every day on: @@ -9,7 +9,7 @@ jobs: clean: runs-on: ubuntu-latest steps: - - name: cleanup + - name: 🧹 Cleanup old workflow artifacts uses: kolpav/purge-artifacts-action@v1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d39190cc..9ed68369 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,4 +1,8 @@ -name: "CodeQL" +name: 📊 CodeQL +run-name: | + ${{ github.event_name == 'schedule' && '⏰ Scheduled CodeQL run' || '' }} + ${{ github.event_name == 'push' && format('📊 Pushed CodeQL run - {0}', github.event.head_commit.message) || '' }} + ${{ github.event_name == 'pull_request' && format('📊 CodeQL run for PR {0} - {1}', github.event.pull_request.number, github.event.pull_request.title) || github.event.head_commit.message }} on: push: @@ -10,7 +14,7 @@ on: jobs: analyze: - name: Analyze + name: 📊 Analyze runs-on: ubuntu-latest permissions: actions: read @@ -23,35 +27,35 @@ jobs: language: [ python, javascript, cpp ] steps: - - name: Checkout + - name: ⬇ Checkout uses: actions/checkout@v4 with: submodules: recursive - - name: Install Packages (cpp) + - name: 📥 Install Packages (cpp) if: ${{ matrix.language == 'cpp' }} run: | sudo apt-get update sudo apt-get install --yes git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev - - name: Initialize CodeQL + - name: 🔁 Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} queries: +security-and-quality config-file: ./.github/config/codeql.yml - - name: Autobuild + - name: 👷 Autobuild uses: github/codeql-action/autobuild@v2 - - name: Perform CodeQL Analysis + - name: 🏃 Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 with: category: "/language:${{ matrix.language }}" upload: False output: sarif-results - - name: Filter SARIF + - name: 🆔 Filter SARIF uses: advanced-security/filter-sarif@v1 with: patterns: | @@ -63,11 +67,12 @@ jobs: input: sarif-results/${{ matrix.language }}.sarif output: sarif-results/${{ matrix.language }}.sarif - - name: Upload SARIF + - name: 📦 Upload SARIF uses: github/codeql-action/upload-sarif@v2 with: sarif_file: sarif-results/${{ matrix.language }}.sarif - - name: Upload loc as a Build Artifact + + - name: 📦 Upload loc as a Build Artifact uses: actions/upload-artifact@v3 with: name: sarif-results diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml deleted file mode 100644 index 2046ba07..00000000 --- a/.github/workflows/nightly.yml +++ /dev/null @@ -1,187 +0,0 @@ -name: Nightly build - -# Create nightly builds at the end of every day -on: - schedule: - - cron: '0 0 * * *' - repository_dispatch: - types: [hyperion_nightly_push] - -jobs: - update: - name: Update Submodule rpi_ws281x - if: github.repository_owner == 'hyperion-project' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - fetch-depth: 0 - submodules: recursive - - - name: Update Submodule rpi_ws281x - id: update - run: git submodule update --remote --recursive dependencies/external/rpi_ws281x - - - name: Check git status - id: status - run: echo "status=$(git status -s)" >> $GITHUB_OUTPUT - - - name: Add and commit changes - if: ${{ steps.status.outputs.status }} - run: | - git config --local user.email "20935312+Hyperion-Bot@users.noreply.github.com" - git config --local user.name "Hyperion-Bot" - git config --local diff.ignoreSubmodules dirty - git commit -am "Update submodule rpi_ws281x" - - - name: Push changes - if: ${{ steps.status.outputs.status }} - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.HYPERION_BOT_TOKEN }} - branch: ${{ github.ref }} - - check: - name: Compare local <-> nightly - needs: [update] - if: github.repository_owner == 'hyperion-project' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Check if commit has changed - id: build-necessary - run: | - if wget --spider "https://nightly.apt.hyperion-project.org/$(git rev-parse --short HEAD)" 2>/dev/null; then - echo "commit-has-changed=false" >> $GITHUB_OUTPUT - else - echo "commit-has-changed=true" >> $GITHUB_OUTPUT - fi - outputs: - build-nightly: ${{ steps.build-necessary.outputs.commit-has-changed }} - - setup: - name: Setup nightly build - needs: [check] - if: ${{ needs.check.outputs.build-nightly == 'true' }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Set nightly matrix - id: nightly-ppa - run: | - NIGHTLY=$(jq -n '.include |= [ inputs[] | select(.["exclude"] != true)]' .github/workflows/apt/*.json --compact-output) - echo "nightly=$NIGHTLY" >> $GITHUB_OUTPUT - - outputs: - nightly-matrix: ${{ steps.nightly-ppa.outputs.nightly }} - - build: - name: ${{ matrix.description }} - needs: [setup] - runs-on: ubuntu-latest - strategy: - matrix: ${{ fromJson(needs.setup.outputs.nightly-matrix) }} - - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Generate environment variables - run: | - echo "$(tr -d '\n' < .version)+nightly$(date '+%Y%m%d')$(git rev-parse --short HEAD)" > .version - VERSION=$(cat .version) - echo VERSION=${VERSION} >> $GITHUB_ENV - if [[ $VERSION == *"-"* ]]; then - echo STANDARDS_VERSION=$(echo ${VERSION%-*}) >> $GITHUB_ENV - echo TARBALL_VERSION=$(echo ${VERSION%-*}) >> $GITHUB_ENV - echo DEBIAN_FORMAT='3.0 (quilt)' >> $GITHUB_ENV - else - echo STANDARDS_VERSION=$(echo ${VERSION%+*}) >> $GITHUB_ENV - echo TARBALL_VERSION=${VERSION}~$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV - echo DEBIAN_FORMAT='3.0 (native)' >> $GITHUB_ENV - fi - echo DISTRIBUTION=$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV - - - name: Build package - shell: bash - run: | - mkdir -p "${GITHUB_WORKSPACE}/deploy" - docker run --rm \ - -v "${GITHUB_WORKSPACE}/deploy:/deploy" \ - -v "${GITHUB_WORKSPACE}:/source:rw" \ - ghcr.io/hyperion-project/${{ matrix.architecture }}:${{ env.DISTRIBUTION }} \ - /bin/bash -c "cd /source && \ - mkdir -p debian/source && echo '${{ env.DEBIAN_FORMAT }}' > debian/source/format && \ - dch --create --distribution ${{ env.DISTRIBUTION }} --package 'hyperion' -v '${{ env.VERSION }}~${{ env.DISTRIBUTION }}' '${{ github.event.commits[0].message }}' && \ - cp -fr LICENSE debian/copyright && \ - sed 's/@BUILD_DEPENDS@/${{ matrix.build-depends }}/g; s/@DEPENDS@/${{ matrix.package-depends }}/g; s/@ARCHITECTURE@/${{ matrix.architecture }}/g; s/@STANDARDS_VERSION@/${{ env.STANDARDS_VERSION }}/g' debian/control.in > debian/control && \ - sed 's/@CMAKE_ENVIRONMENT@/${{ matrix.cmake-environment }}/g' debian/rules.in > debian/rules && \ - tar -cJf ../hyperion_${{ env.TARBALL_VERSION }}.orig.tar.xz . && \ - debuild --no-lintian -uc -us && \ - cp ../hyperion_*.deb /deploy" - - - name: Upload package artifact - uses: actions/upload-artifact@v3 - with: - path: deploy - retention-days: 1 - - publish: - name: Publish nightly packages - needs: [setup, build] - if: github.repository_owner == 'hyperion-project' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v6.0.0 - with: - gpg_private_key: ${{ secrets.APT_GPG }} - - - name: Install reprepro - run: sudo apt -y install reprepro - - - name: Make build folders, export public GPG key, copy distributions file and create short sha file for nightly build check - run: | - mkdir -p nightly/{conf,dists,db} - gpg --armor --output nightly/hyperion.pub.key --export 'admin@hyperion-project.org' - cp debian/distributions nightly/conf/distributions - touch "nightly/$(git rev-parse --short HEAD)" - - - name: Create initial structure/packages files and symbolic links - run: | - reprepro -Vb nightly createsymlinks - reprepro -Vb nightly export - - - name: Download artifacts - uses: actions/download-artifact@v3.0.2 - - - name: Include artifacts into the package source - run: | - for file in artifact/*.deb; do - if [ -f "$file" ]; then - dist=${file#*~} - dist=${dist%_*} - reprepro -Vb nightly/ includedeb "$dist" "$file" - fi - done - - - name: Upload packages to nightly server - uses: SamKirkland/FTP-Deploy-Action@v4.3.4 - with: - server: nightly.apt.hyperion-project.org - username: ${{ secrets.NIGHTLY_USER }} - password: ${{ secrets.NIGHTLY_PASSWORD }} - local-dir: "./nightly/" - server-dir: "./" - dangerous-clean-slate: true - - - name: Remove intermediate artifacts - uses: geekyeggo/delete-artifact@v2 - with: - name: artifact - failOnError: false diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml deleted file mode 100644 index eee7c150..00000000 --- a/.github/workflows/pull-request.yml +++ /dev/null @@ -1,193 +0,0 @@ -name: Hyperion PR Build -on: - pull_request: - branches: - - master - -jobs: - -###################### -###### Linux ######### -###################### - - Linux: - name: ${{ matrix.dockerName }} - runs-on: ubuntu-latest - strategy: - matrix: - dockerImage: [ x86_64, armv6l, armv7l, aarch64 ] - include: - - dockerImage: x86_64 - dockerName: Debian Buster (x86_64) - platform: x11 - - dockerImage: armv6l - dockerName: Debian Buster (Raspberry Pi v1 & ZERO) - platform: rpi - - dockerImage: armv7l - dockerName: Debian Buster (Raspberry Pi 2 & 3) - platform: rpi - - dockerImage: aarch64 - dockerName: Debian Buster (Generic AARCH64) - platform: amlogic - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - # Append PR number to .version - - name: Append PR number to version - shell: bash - run: | - tr -d '\n' < .version > temp && mv temp .version - echo -n "+PR${{ github.event.pull_request.number }}" >> .version - - # Build packages - - name: Build packages - env: - DOCKER_IMAGE: ${{ matrix.dockerImage }} - DOCKER_TAG: buster - DOCKER_NAME: ${{ matrix.dockerName }} - PLATFORM: ${{ matrix.platform }} - shell: bash - run: ./.ci/ci_build.sh - - # Collecting deployable artifacts - - name: Collecting deployable artifacts - shell: bash - run: | - mkdir -p ${{ matrix.dockerImage }} - mv deploy/*.tar.gz ${{ matrix.dockerImage }} - - # Upload artifacts - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.dockerImage }} - path: ${{ matrix.dockerImage }} - -###################### -###### macOS ######### -###################### - - macOS: - name: macOS - runs-on: macos-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - # Append PR number to .version - - name: Append PR number to version - shell: bash - run: | - tr -d '\n' < .version > temp && mv temp .version - echo -n "+PR${{ github.event.pull_request.number }}" >> .version - - # Install dependencies - - name: Install dependencies - shell: bash - run: ./.ci/ci_install.sh - - # Build packages - - name: Build packages - env: - PLATFORM: osx - shell: bash - run: ./.ci/ci_build.sh - - # Collecting deployable artifacts - - name: Collecting deployable artifacts - shell: bash - run: | - mkdir -p macOS - mv build/*.dmg macOS - - # Upload artifacts - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: macOS - path: macOS - -###################### -###### Windows ####### -###################### - - windows: - name: Windows - runs-on: windows-2022 - env: - VCINSTALLDIR: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC' - QT_VERSION: 5.15.2 - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - # Append PR number to .version - - name: Append PR number to version - shell: bash - run: | - tr -d '\n' < .version > temp && mv temp .version - echo -n "+PR${{ github.event.pull_request.number }}" >> .version - - - name: Install Qt - uses: jurplel/install-qt-action@v3 - with: - version: ${{env.QT_VERSION}} - target: 'desktop' - arch: 'win64_msvc2019_64' - cache: 'true' - cache-key-prefix: 'cache-qt-windows' - - - name: Cache Chocolatey downloads - uses: actions/cache@v3 - with: - path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey - key: ${{ runner.os }}-chocolatey - -# - name: Install Python -# shell: powershell -# run: | -# choco install --no-progress python -y - - - name: Install OpenSSL, DirectX SDK - shell: powershell - run: | - choco install --no-progress openssl --version=1.1.1.2100 -y - choco install --no-progress directx-sdk -y - - - name: Install libjpeg-turbo - run: | - Invoke-WebRequest https://netcologne.dl.sourceforge.net/project/libjpeg-turbo/2.0.6/libjpeg-turbo-2.0.6-vc64.exe -OutFile libjpeg-turbo.exe -UserAgent NativeHost - .\libjpeg-turbo /S - - - name: Set up x64 build architecture environment - shell: cmd - run: call "${{env.VCINSTALLDIR}}\Auxiliary\Build\vcvars64.bat" - - # Build packages - - name: Build packages - env: - PLATFORM: windows - shell: bash - run: ./.ci/ci_build.sh - - # Collecting deployable artifacts - - name: Collecting deployable artifacts - shell: bash - run: | - mkdir -p windows - mv build/*.exe windows - - # Upload artifacts - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: windows - path: windows diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml deleted file mode 100644 index b0ee7a1d..00000000 --- a/.github/workflows/push-master.yml +++ /dev/null @@ -1,201 +0,0 @@ -name: Hyperion CI Build -on: - push: - branches: - - '**' - tags: - - '*' - -jobs: - -################### -###### Linux ###### -################### - - Linux: - name: ${{ matrix.dockerName }} - runs-on: ubuntu-latest - strategy: - matrix: - dockerImage: [ x86_64, armv6l, armv7l, aarch64 ] - include: - - dockerImage: x86_64 - dockerName: Debian Buster (x86_64) - platform: x11 - - dockerImage: armv6l - dockerName: Debian Buster (Raspberry Pi v1 & ZERO) - platform: rpi - - dockerImage: armv7l - dockerName: Debian Buster (Raspberry Pi 2 & 3) - platform: rpi - - dockerImage: aarch64 - dockerName: Debian Buster (Generic AARCH64) - platform: amlogic - - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - # Build process - - name: Build packages - env: - DOCKER_IMAGE: ${{ matrix.dockerImage }} - DOCKER_TAG: buster - DOCKER_NAME: ${{ matrix.dockerName }} - PLATFORM: ${{ matrix.platform }} - shell: bash - run: ./.ci/ci_build.sh - - # Upload artifacts (only on tagged commit) - - name: Upload artifacts - if: startsWith(github.event.ref, 'refs/tags') - uses: actions/upload-artifact@v3 - with: - path: deploy/Hyperion-* - -################### -###### macOS ###### -################### - - macOS: - name: macOS - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - # Install dependencies - - name: Install dependencies - shell: bash - run: ./.ci/ci_install.sh - - # Build process - - name: Build packages - env: - PLATFORM: osx - shell: bash - run: ./.ci/ci_build.sh - - # Upload artifacts (only on tagged commit) - - name: Upload artifacts - if: startsWith(github.event.ref, 'refs/tags') - uses: actions/upload-artifact@v3 - with: - path: build/Hyperion-* - -##################### -###### Windows ###### -##################### - - windows: - name: Windows - runs-on: windows-2022 - env: - VCINSTALLDIR: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC' - QT_VERSION: 5.15.2 - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install Qt - uses: jurplel/install-qt-action@v3 - with: - version: ${{env.QT_VERSION}} - target: 'desktop' - arch: 'win64_msvc2019_64' - cache: 'true' - cache-key-prefix: 'cache-qt-windows' - - - name: Cache Chocolatey downloads - uses: actions/cache@v3 - with: - path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey - key: ${{ runner.os }}-chocolatey - -# - name: Install Python -# shell: powershell -# run: | -# choco install --no-progress python -y - - - name: Install OpenSSL, DirectX SDK - shell: powershell - run: | - choco install --no-progress openssl --version=1.1.1.2100 -y - choco install --no-progress directx-sdk -y - - - name: Install libjpeg-turbo - run: | - Invoke-WebRequest https://netcologne.dl.sourceforge.net/project/libjpeg-turbo/2.0.6/libjpeg-turbo-2.0.6-vc64.exe -OutFile libjpeg-turbo.exe -UserAgent NativeHost - .\libjpeg-turbo /S - - - name: Set up x64 build architecture environment - shell: cmd - run: call "${{env.VCINSTALLDIR}}\Auxiliary\Build\vcvars64.bat" - - # Build packages - - name: Build packages - env: - PLATFORM: windows - shell: bash - run: ./.ci/ci_build.sh - - # Upload artifacts (only on tagged commit) - - name: Upload artifacts - if: startsWith(github.event.ref, 'refs/tags') - uses: actions/upload-artifact@v3 - with: - path: build/Hyperion-* - retention-days: 1 - -##################################### -###### Publish GitHub Releases ###### -##################################### - - github_publish: - name: Publish GitHub Releases - if: startsWith(github.event.ref, 'refs/tags') - needs: [Linux, macOS, windows] - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - # Generate environment variables - - name: Generate environment variables from .version and tag - run: | - echo "TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV - echo "VERSION=$(tr -d '\n' < .version)" >> $GITHUB_ENV - - # Download artifacts from previous build process - - name: Download artifacts - uses: actions/download-artifact@v3.0.2 - with: - path: artifacts - - # Create draft release and upload artifacts - - name: Create draft release - uses: softprops/action-gh-release@v1 - with: - name: Hyperion ${{ env.VERSION }} - tag_name: ${{ env.TAG }} - files: "artifacts/**" - draft: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - -########################## -###### APT workflow ###### -########################## - - apt_build: - name: APT Build GitHub Releases - if: startsWith(github.event.ref, 'refs/tags') - needs: [Linux, macOS, windows] - uses: ./.github/workflows/apt.yml - secrets: inherit - with: - head_sha: master diff --git a/.github/workflows/push_pull.yml b/.github/workflows/push_pull.yml new file mode 100644 index 00000000..add2c944 --- /dev/null +++ b/.github/workflows/push_pull.yml @@ -0,0 +1,49 @@ +name: Hyperion CI/PR Builds +run-name: | + ${{ github.event_name == 'push' && '🌱 Push build -' || '' }} + ${{ github.event_name == 'pull_request' && format('📦 Artifacts build for PR {0} - {1}', github.event.pull_request.number, github.event.pull_request.title) || github.event.head_commit.message }} + +on: + push: + branches: + - '**' + tags: + - '*' + pull_request: + branches: + - 'master' + +jobs: + + # GitHub Push/Pull Request (Release only on tagged commits) + github_build: + name: Qt ${{ matrix.qt_version }} Build ${{ matrix.qt_version == '6' && '(Testing))' || '' }} + strategy: + fail-fast: false + matrix: + qt_version: ['5', '6'] + uses: ./.github/workflows/qt5_6.yml + secrets: inherit + with: + qt_version: ${{ matrix.qt_version }} + event_name: ${{ github.event_name }} + pull_request_number: ${{ github.event.pull_request.number }} + publish: ${{ startsWith(github.event.ref, 'refs/tags') }} + + # Build DEB/RPM Packages for APT/DNF Repository (runs only on tagged commits) + repo_build: + name: 🚀 Let Hyperion build its own repository (APT/DNF) + if: startsWith(github.event.ref, 'refs/tags') + needs: [ github_build ] + runs-on: ubuntu-latest + steps: + - name: 📲 Dispatch APT/DNF build + if: ${{ env.SECRET_HYPERION_BOT_TOKEN != null }} + uses: peter-evans/repository-dispatch@v2.1.2 + with: + repository: hyperion-project/hyperion.releases-ci + token: ${{ secrets.HYPERION_BOT_TOKEN }} + event-type: releases_repo_build + client-payload: '{ "head_sha": "${{ github.sha }}" }' + env: + SECRET_HYPERION_BOT_TOKEN: ${{ secrets.HYPERION_BOT_TOKEN }} diff --git a/.github/workflows/qt5_6.yml b/.github/workflows/qt5_6.yml new file mode 100644 index 00000000..ac4622b3 --- /dev/null +++ b/.github/workflows/qt5_6.yml @@ -0,0 +1,250 @@ +name: GitHub Qt5/6 Builds + +on: + # Reusable from push_pull.yml + workflow_call: + inputs: + qt_version: + type: string + description: Build with this Qt version + default: '5' + required: false + event_name: + type: string + description: The event name + default: '' + required: false + pull_request_number: + type: string + description: The corresponding PR number + default: '' + required: false + publish: + type: boolean + description: Package publishing + default: false + required: false + +env: + ghcr: hyperion-project + +jobs: + +###################### +###### Linux ######### +###################### + + Linux: + name: 🐧 ${{ matrix.os.description }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: [ + { distribution: debian, codename: buster, description: Debian Buster (x86_64), architecture: [ amd64, linux/amd64 ] }, + { distribution: debian, codename: bullseye, description: Debian Bullseye (x86_64), architecture: [ amd64, linux/amd64 ] }, + { distribution: debian, codename: buster, description: Debian Buster (Raspberry Pi 1/ZERO), architecture: [ armv6, linux/arm/v5 ] }, + { distribution: debian, codename: buster, description: Debian Buster (Raspberry Pi 2/3/4), architecture: [ armv7, linux/arm/v7 ] }, + { distribution: debian, codename: bullseye, description: Debian Bullseye (Raspberry Pi 2/3/4), architecture: [ armv7, linux/arm/v7 ] }, + { distribution: debian, codename: buster, description: Debian Buster (Generic AARCH64), architecture: [ aarch64, linux/arm64 ] }, + { distribution: debian, codename: bullseye, description: Debian Bullseye (Generic AARCH64), architecture: [ aarch64, linux/arm64 ] } + ] + isQt5: + - ${{ inputs.qt_version == '5' }} + include: + - os.architecture[0]: amd64 + platform: x11 + - os.architecture[0]: armv6 + platform: rpi + - os.architecture[0]: armv7 + platform: rpi + - os.architecture[0]: aarch64 + platform: amlogic + exclude: + - isQt5: true + os: { distribution: debian, codename: bullseye } + - isQt5: false + os: { distribution: debian, codename: buster } + + steps: + - name: ⬇ Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: 🔧 Prepare + shell: bash + run: | + echo '::group::Append PR number to version (PR only)' + if [[ "${{ inputs.event_name }}" = "pull_request" ]]; then + tr -d '\n' < .version > temp && mv temp .version + echo -n "+PR${{ inputs.pull_request_number }}" >> .version + fi + echo '::endgroup::' + + - name: 👷 Build + shell: bash + run: ./.github/scripts/build.sh + env: + DOCKER_IMAGE: ${{ matrix.os.distribution }} + DOCKER_TAG: ${{ matrix.os.codename }}${{ inputs.qt_version == '6' && '-qt6' || '' }} + PLATFORM: ${{ matrix.platform }} + TARGET_ARCH: ${{ matrix.os.architecture[1] }} + + - name: 📦 Upload + if: ${{ inputs.publish || inputs.event_name == 'pull_request' }} + uses: actions/upload-artifact@v3 + with: + name: ${{ inputs.event_name == 'pull_request' && env.NAME || 'artifact' }} + path: ${{ inputs.event_name == 'pull_request' && 'deploy/*.tar.gz' || 'deploy/Hyperion-*' }} + env: + NAME: ${{ format('{0}_{1}_{2}{3}', matrix.os.distribution, matrix.os.codename, matrix.os.architecture[0], inputs.qt_version == '6' && '_qt6' || '') }} + +###################### +###### macOS ######### +###################### + + macOS: + name: 🍏 macOS x64 + runs-on: macos-latest + steps: + - name: ⬇ Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: 🔧 Prepare + shell: bash + run: | + echo '::group::Append PR number to version (PR only)' + if [[ "${{ inputs.event_name }}" = "pull_request" ]]; then + tr -d '\n' < .version > temp && mv temp .version + echo -n "+PR${{ inputs.pull_request_number }}" >> .version + fi + echo '::endgroup::' + + echo '::group::Update/Install dependencies' + brew update || true + brew install --overwrite qt${{ inputs.qt_version }} libusb + brew link --overwrite --force qt${{ inputs.qt_version }} + echo '::endgroup::' + + - name: 👷 Build + shell: bash + run: ./.github/scripts/build.sh + env: + PLATFORM: osx + + - name: 📦 Upload + if: ${{ inputs.publish || inputs.event_name == 'pull_request' }} + uses: actions/upload-artifact@v3 + with: + name: ${{ inputs.event_name == 'pull_request' && env.NAME || 'artifact' }} + path: 'build/Hyperion-*' + env: + NAME: ${{ inputs.qt_version == '6' && 'macOS_x64_qt6' || 'macOS_x64' }} + +###################### +###### Windows ####### +###################### + + windows: + name: 🪟 Windows x64 + runs-on: windows-2022 + env: + VCINSTALLDIR: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC' + steps: + - name: ⬇ Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: 🔧 Prepare PR + if: ${{ inputs.event_name == 'pull_request' }} + shell: bash + run: | + echo '::group::Append PR number to version' + tr -d '\n' < .version > temp && mv temp .version + echo -n "+PR${{ inputs.pull_request_number }}" >> .version + echo '::endgroup::' + + - name: 💾 Cache/Restore + uses: actions/cache@v3 + with: + path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey + key: ${{ runner.os }}${{ inputs.qt_version == '6' && '-chocolatey-qt6' || '-chocolatey' }} + + - name: 📥 Install DirectX SDK, OpenSSL, libjpeg-turbo ${{ inputs.qt_version == '6' && 'and Vulkan-SDK' || '' }} + shell: powershell + run: | + choco install --no-progress directx-sdk ${{env.VULKAN_SDK}} -y + choco install --no-progress ${{env.OPENSSL}} -y + Invoke-WebRequest https://netcologne.dl.sourceforge.net/project/libjpeg-turbo/3.0.1/libjpeg-turbo-3.0.1-vc64.exe -OutFile libjpeg-turbo.exe -UserAgent NativeHost + .\libjpeg-turbo /S + env: + VULKAN_SDK: ${{ inputs.qt_version == '6' && 'vulkan-sdk' || '' }} + OPENSSL: ${{ inputs.qt_version == '6' && 'openssl' || 'openssl --version=1.1.1.2100' }} + + - name: 📥 Install Qt + uses: jurplel/install-qt-action@v3 + with: + version: ${{ inputs.qt_version == '6' && '6.5.2' || '5.15.2' }} + target: 'desktop' + modules: ${{ inputs.qt_version == '6' && 'qtserialport' || '' }} + arch: 'win64_msvc2019_64' + cache: 'true' + cache-key-prefix: 'cache-qt-windows' + + - name: 🛠️ Setup MSVC + shell: cmd + run: call "${{env.VCINSTALLDIR}}\Auxiliary\Build\vcvars64.bat" + + - name: 👷 Build + shell: bash + run: ./.github/scripts/build.sh + env: + PLATFORM: windows + + - name: 📦 Upload + if: ${{ inputs.publish || inputs.event_name == 'pull_request' }} + uses: actions/upload-artifact@v3 + with: + name: ${{ inputs.event_name == 'pull_request' && env.NAME || 'artifact' }} + path: ${{ inputs.event_name == 'pull_request' && 'build/*.exe' || 'build/Hyperion-*' }} + env: + NAME: ${{ inputs.qt_version == '6' && 'windows_x64_qt6' || 'windows_x64' }} + +##################################### +###### Publish GitHub Releases ###### +##################################### + + github_publish: + name: 🚀 Publish GitHub Releases + if: ${{ inputs.qt_version == '5' && inputs.publish }} + needs: [Linux, macOS, windows] + runs-on: ubuntu-latest + steps: + - name: ⬇ Checkout + uses: actions/checkout@v4 + + - name: 🔧 Prepare + run: | + echo '::group::Generate environment variables from .version and tag' + echo "TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV + echo "VERSION=$(tr -d '\n' < .version)" >> $GITHUB_ENV + echo '::endgroup::' + + - name: 💾 Artifact download + uses: actions/download-artifact@v3.0.2 + with: + path: artifacts + + - name: 📦 Upload + uses: softprops/action-gh-release@v1 + with: + name: Hyperion ${{ env.VERSION }} + tag_name: ${{ env.TAG }} + files: "artifacts/**" + draft: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 104c2c79..941e07dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,15 +1,18 @@ -name: Release Actions +name: 🚀 Release Actions +run-name: 🚀 Let HyperBian create + on: release: types: [published] jobs: + hyperbian: - name: Let HyperBian create + name: 🚀 Let HyperBian create runs-on: ubuntu-latest steps: # Dispatch event to build new HyperBian image - - name: Dispatch HyperBian build + - name: 📲 Dispatch HyperBian build uses: peter-evans/repository-dispatch@v2.1.2 if: ${{ github.repository_owner == 'hyperion-project'}} with: diff --git a/CMakeLists.txt b/CMakeLists.txt index 32bbb7d2..8d88c41e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,17 +1,17 @@ cmake_minimum_required(VERSION 3.5.0) -message( STATUS "CMake Version: ${CMAKE_VERSION}" ) +message(STATUS "CMake Version: ${CMAKE_VERSION}") macro(addIndent text) -if(${CMAKE_VERSION} VERSION_GREATER "3.16.0") - list(APPEND CMAKE_MESSAGE_INDENT ${text}) -endif() + if(${CMAKE_VERSION} VERSION_GREATER "3.16.0") + list(APPEND CMAKE_MESSAGE_INDENT ${text}) + endif() endmacro() macro(removeIndent) -if(${CMAKE_VERSION} VERSION_GREATER "3.16.0") - list(POP_BACK CMAKE_MESSAGE_INDENT) -endif() + if(${CMAKE_VERSION} VERSION_GREATER "3.16.0") + list(POP_BACK CMAKE_MESSAGE_INDENT) + endif() endmacro() PROJECT(hyperion) @@ -31,9 +31,16 @@ set(CMAKE_AUTOMOC ON) # auto prepare .qrc files set(CMAKE_AUTORCC ON) -# Configure CCache if available +# multicore compiling +include(ProcessorCount) +ProcessorCount(NCORES) +if(NOT NCORES EQUAL 0) + set(CMAKE_BUILD_PARALLEL_LEVEL NCORES) +endif() + +# Configure CCache ifavailable find_program(CCACHE_FOUND ccache) -if ( CCACHE_FOUND ) +if(CCACHE_FOUND) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif(CCACHE_FOUND) @@ -58,160 +65,163 @@ set(CMAKE_CXX_EXTENSIONS OFF) # Set build variables # Grabber -SET ( DEFAULT_AMLOGIC OFF ) -SET ( DEFAULT_DISPMANX OFF ) -SET ( DEFAULT_DX OFF ) -SET ( DEFAULT_MF OFF ) -SET ( DEFAULT_OSX OFF ) -SET ( DEFAULT_QT ON ) -SET ( DEFAULT_V4L2 OFF ) -SET ( DEFAULT_AUDIO ON ) -SET ( DEFAULT_X11 OFF ) -SET ( DEFAULT_XCB OFF ) +set(DEFAULT_AMLOGIC OFF) +set(DEFAULT_DISPMANX OFF) +set(DEFAULT_DX OFF) +set(DEFAULT_MF OFF) +set(DEFAULT_OSX OFF) +set(DEFAULT_QT ON ) +set(DEFAULT_V4L2 OFF) +set(DEFAULT_AUDIO ON ) +set(DEFAULT_X11 OFF) +set(DEFAULT_XCB OFF) # Input -SET ( DEFAULT_BOBLIGHT_SERVER ON ) -SET ( DEFAULT_CEC OFF ) -SET ( DEFAULT_FLATBUF_SERVER ON ) -SET ( DEFAULT_PROTOBUF_SERVER ON ) +set(DEFAULT_BOBLIGHT_SERVER ON ) +set(DEFAULT_CEC OFF) +set(DEFAULT_FLATBUF_SERVER ON ) +set(DEFAULT_PROTOBUF_SERVER ON ) # Output -SET ( DEFAULT_FORWARDER ON ) -SET ( DEFAULT_FLATBUF_CONNECT ON ) +set(DEFAULT_FORWARDER ON ) +set(DEFAULT_FLATBUF_CONNECT ON ) # LED-Devices -SET ( DEFAULT_DEV_NETWORK ON ) -SET ( DEFAULT_DEV_SERIAL ON ) -SET ( DEFAULT_DEV_SPI OFF ) -SET ( DEFAULT_DEV_TINKERFORGE OFF ) -SET ( DEFAULT_DEV_USB_HID OFF ) -SET ( DEFAULT_DEV_WS281XPWM OFF ) +set(DEFAULT_DEV_NETWORK ON ) +set(DEFAULT_DEV_SERIAL ON ) +set(DEFAULT_DEV_SPI OFF) +set(DEFAULT_DEV_TINKERFORGE OFF) +set(DEFAULT_DEV_USB_HID OFF) +set(DEFAULT_DEV_WS281XPWM OFF) # Services -SET ( DEFAULT_EFFECTENGINE ON ) -SET ( DEFAULT_EXPERIMENTAL OFF ) -SET ( DEFAULT_MDNS ON ) -SET ( DEFAULT_REMOTE_CTL ON ) +set(DEFAULT_EFFECTENGINE ON ) +set(DEFAULT_EXPERIMENTAL OFF) +set(DEFAULT_MDNS ON ) +set(DEFAULT_REMOTE_CTL ON ) # Build -SET ( DEFAULT_JSONCHECKS ON ) -SET ( DEFAULT_DEPLOY_DEPENDENCIES ON ) -SET ( DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS OFF ) -SET ( DEFAULT_USE_SYSTEM_PROTO_LIBS OFF ) -SET ( DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF ) -SET ( DEFAULT_USE_SYSTEM_QMDNS_LIBS OFF ) -SET ( DEFAULT_TESTS OFF ) +set(DEFAULT_JSONCHECKS ON ) +set(DEFAULT_DEPLOY_DEPENDENCIES ON ) +set(DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS OFF) +set(DEFAULT_USE_SYSTEM_PROTO_LIBS OFF) +set(DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF) +set(DEFAULT_USE_SYSTEM_QMDNS_LIBS OFF) +set(DEFAULT_TESTS OFF) # Build Hyperion with a reduced set of functionality, overwrites other default values -SET ( DEFAULT_HYPERION_LIGHT OFF ) +set(DEFAULT_HYPERION_LIGHT OFF) -IF ( ${CMAKE_SYSTEM} MATCHES "Linux" ) - SET ( DEFAULT_FB ON ) - SET ( DEFAULT_V4L2 ON ) - SET ( DEFAULT_DEV_SPI ON ) - SET ( DEFAULT_DEV_TINKERFORGE ON ) - SET ( DEFAULT_DEV_USB_HID ON ) - SET ( DEFAULT_CEC ON ) -ELSEIF ( WIN32 ) - SET ( DEFAULT_DX ON ) - SET ( DEFAULT_MF ON ) -ELSE() - SET ( DEFAULT_FB OFF ) - SET ( DEFAULT_V4L2 OFF ) - SET ( DEFAULT_DEV_SPI OFF ) - SET ( DEFAULT_DEV_TINKERFORGE OFF ) - SET ( DEFAULT_DEV_USB_HID OFF ) - SET ( DEFAULT_CEC OFF ) -ENDIF() +if(${CMAKE_SYSTEM} MATCHES "Linux") + set(DEFAULT_FB ON) + set(DEFAULT_V4L2 ON) + set(DEFAULT_DEV_SPI ON) + set(DEFAULT_DEV_TINKERFORGE ON) + set(DEFAULT_DEV_USB_HID ON) + set(DEFAULT_CEC ON) +elseif (WIN32) + set(DEFAULT_DX ON) + set(DEFAULT_MF ON) +else() + set(DEFAULT_FB OFF) + set(DEFAULT_V4L2 OFF) + set(DEFAULT_DEV_SPI OFF) + set(DEFAULT_DEV_TINKERFORGE OFF) + set(DEFAULT_DEV_USB_HID OFF) + set(DEFAULT_CEC OFF) +endif() -if ( NOT DEFINED PLATFORM ) - if ( APPLE ) - SET( PLATFORM "osx") - elseif ( WIN32 ) - SET( PLATFORM "windows") - elseif ( "${CMAKE_SYSTEM_PROCESSOR}" MATCHES "x86" ) - SET( PLATFORM "x11") - elseif ( "${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm" OR "${CMAKE_SYSTEM_PROCESSOR}" MATCHES "aarch64") - SET( PLATFORM "rpi") - FILE( READ /proc/cpuinfo SYSTEM_CPUINFO ) - STRING ( TOLOWER "${SYSTEM_CPUINFO}" SYSTEM_CPUINFO ) - if ( "${SYSTEM_CPUINFO}" MATCHES "amlogic" AND ${CMAKE_SIZEOF_VOID_P} EQUAL 4 ) - SET( PLATFORM "amlogic" ) - elseif ( ("${SYSTEM_CPUINFO}" MATCHES "amlogic" OR "${SYSTEM_CPUINFO}" MATCHES "odroid-c2" OR "${SYSTEM_CPUINFO}" MATCHES "vero4k") AND ${CMAKE_SIZEOF_VOID_P} EQUAL 8 ) - SET( PLATFORM "amlogic64" ) +if(NOT DEFINED PLATFORM) + if(APPLE) + set(PLATFORM "osx") + elseif (WIN32) + set(PLATFORM "windows") + elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "x86") + set(PLATFORM "x11") + elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm" OR "${CMAKE_SYSTEM_PROCESSOR}" MATCHES "aarch64") + set(PLATFORM "rpi") + file(READ /proc/cpuinfo SYSTEM_CPUINFO) + STRING (TOLOWER "${SYSTEM_CPUINFO}" SYSTEM_CPUINFO) + if("${SYSTEM_CPUINFO}" MATCHES "amlogic" AND ${CMAKE_SIZEOF_VOID_P} EQUAL 4) + set(PLATFORM "amlogic") + elseif (("${SYSTEM_CPUINFO}" MATCHES "amlogic" OR "${SYSTEM_CPUINFO}" MATCHES "odroid-c2" OR "${SYSTEM_CPUINFO}" MATCHES "vero4k") AND ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + set(PLATFORM "amlogic64") endif() endif() - if ( PLATFORM ) - message( STATUS "PLATFORM is not defined, evaluated platform: ${PLATFORM}") + if(PLATFORM) + message(STATUS "PLATFORM is not defined, evaluated platform: ${PLATFORM}") else() - message( FATAL_ERROR "PLATFORM is not defined and could not be evaluated. Set -DPLATFORM=") + message(FATAL_ERROR "PLATFORM is not defined and could not be evaluated. Set -DPLATFORM=") endif() endif() -message( STATUS "PLATFORM: ${PLATFORM}") +message(STATUS "PLATFORM: ${PLATFORM}") # Macro to get path of first sub dir of a dir, used for MAC OSX lib/header searching -MACRO(FIRSTSUBDIR result curdir) - FILE(GLOB children RELATIVE ${curdir} ${curdir}/*) - SET(dirlist "") - FOREACH(child ${children}) - IF(IS_DIRECTORY ${curdir}/${child}) - LIST(APPEND dirlist "${curdir}/${child}") - BREAK() - ENDIF() - ENDFOREACH() - SET(${result} ${dirlist}) -ENDMACRO() +macro(FIRSTSUBDIR result curdir) + file(GLOB children RELATIVE ${curdir} ${curdir}/*) + set(dirlist "") + foreach(child ${children}) + if(IS_DIRECTORY ${curdir}/${child}) + list(APPEND dirlist "${curdir}/${child}") + break() + endif() + endforeach() + set(${result} ${dirlist}) +endmacro() + +if("${PLATFORM}" MATCHES "osx") + # specify the min version of the target platform (only GitHub Actions) + if(DEFINED ENV{GITHUB_WORKSPACE}) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15") + endif() -if ( "${PLATFORM}" MATCHES "osx" ) - # specify the min version of the target platform - SET ( CMAKE_OSX_DEPLOYMENT_TARGET "10.15" ) # add specific prefix paths FIRSTSUBDIR(SUBDIRPY "/usr/local/opt/python3/Frameworks/Python.framework/Versions") set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${SUBDIRPY}) include_directories("/opt/X11/include/") - SET ( DEFAULT_OSX ON ) - SET ( DEFAULT_AUDIO OFF ) - SET ( DEFAULT_DEV_USB_HID ON ) + set(DEFAULT_OSX ON ) + set(DEFAULT_AUDIO OFF) + set(DEFAULT_DEV_USB_HID ON ) -elseif ( "${PLATFORM}" MATCHES "rpi" ) - SET ( DEFAULT_DISPMANX ON ) - SET ( DEFAULT_DEV_WS281XPWM ON ) -elseif ( "${PLATFORM}" MATCHES "^amlogic" ) - SET ( DEFAULT_AMLOGIC ON ) - if ( "${PLATFORM}" MATCHES "-dev$" ) - SET ( DEFAULT_AMLOGIC ON ) - SET ( DEFAULT_DISPMANX OFF ) - SET ( DEFAULT_QT OFF ) - SET ( DEFAULT_CEC OFF ) +elseif ("${PLATFORM}" MATCHES "rpi") + set(DEFAULT_DISPMANX ON) + set(DEFAULT_DEV_WS281XPWM ON) +elseif ("${PLATFORM}" MATCHES "^amlogic") + set(DEFAULT_AMLOGIC ON) + if("${PLATFORM}" MATCHES "-dev$") + set(DEFAULT_AMLOGIC ON) + set(DEFAULT_DISPMANX OFF) + set(DEFAULT_QT OFF) + set(DEFAULT_CEC OFF) endif() -elseif ( "${PLATFORM}" MATCHES "^x11" ) - SET ( DEFAULT_X11 ON ) - SET ( DEFAULT_XCB ON ) - if ( "${PLATFORM}" MATCHES "-dev$" ) - SET ( DEFAULT_AMLOGIC ON) - SET ( DEFAULT_DEV_WS281XPWM ON ) +elseif ("${PLATFORM}" MATCHES "^x11") + set(DEFAULT_X11 ON) + set(DEFAULT_XCB ON) + if("${PLATFORM}" MATCHES "-dev$") + set(DEFAULT_AMLOGIC ON) + set(DEFAULT_DEV_WS281XPWM ON) endif() -elseif ( "${PLATFORM}" STREQUAL "imx6" ) - SET ( DEFAULT_FB ON ) +elseif ("${PLATFORM}" STREQUAL "imx6") + set(DEFAULT_FB ON) endif() # enable tests for -dev builds -if ( "${PLATFORM}" MATCHES "-dev$" ) - SET ( DEFAULT_TESTS ON ) +if("${PLATFORM}" MATCHES "-dev$") + set(DEFAULT_TESTS ON) endif() -STRING( TOUPPER "-DPLATFORM_${PLATFORM}" PLATFORM_DEFINE) -STRING( REPLACE "-DEV" "" PLATFORM_DEFINE "${PLATFORM_DEFINE}" ) -ADD_DEFINITIONS( ${PLATFORM_DEFINE} ) +string(TOUPPER "-DPLATFORM_${PLATFORM}" PLATFORM_DEFINE) +string(REPLACE "-DEV" "" PLATFORM_DEFINE "${PLATFORM_DEFINE}") +ADD_DEFINITIONS(${PLATFORM_DEFINE}) # set the build options -option(HYPERION_LIGHT "Build Hyperion with a reduced set of functionality" ${DEFAULT_HYPERION_LIGHT} ) +option(HYPERION_LIGHT "Build Hyperion with a reduced set of functionality" ${DEFAULT_HYPERION_LIGHT}) message(STATUS "HYPERION_LIGHT = ${HYPERION_LIGHT}") -if (HYPERION_LIGHT) +if(HYPERION_LIGHT) message(STATUS "HYPERION_LIGHT: Hyperion is build with a reduced set of functionality.") # Disable Grabbers SET ( DEFAULT_AMLOGIC OFF ) @@ -228,43 +238,43 @@ if (HYPERION_LIGHT) SET ( DEFAULT_AUDIO OFF ) # Disable Input Servers - SET ( DEFAULT_BOBLIGHT_SERVER OFF ) - SET ( DEFAULT_CEC OFF ) - SET ( DEFAULT_FLATBUF_SERVER OFF ) - SET ( DEFAULT_PROTOBUF_SERVER OFF ) + set(DEFAULT_BOBLIGHT_SERVER OFF) + set(DEFAULT_CEC OFF) + set(DEFAULT_FLATBUF_SERVER OFF) + set(DEFAULT_PROTOBUF_SERVER OFF) # Disable Output Connectors - SET ( DEFAULT_FORWARDER OFF ) - SET ( DEFAULT_FLATBUF_CONNECT OFF ) + set(DEFAULT_FORWARDER OFF) + set(DEFAULT_FLATBUF_CONNECT OFF) # Disable Services - SET ( DEFAULT_EFFECTENGINE OFF ) + set(DEFAULT_EFFECTENGINE OFF) endif() message(STATUS "Grabber options:") addIndent(" - ") -option(ENABLE_AMLOGIC "Enable the AMLOGIC video grabber" ${DEFAULT_AMLOGIC} ) +option(ENABLE_AMLOGIC "Enable the AMLOGIC video grabber" ${DEFAULT_AMLOGIC}) message(STATUS "ENABLE_AMLOGIC = ${ENABLE_AMLOGIC}") -option(ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ${DEFAULT_DISPMANX} ) +option(ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ${DEFAULT_DISPMANX}) message(STATUS "ENABLE_DISPMANX = ${ENABLE_DISPMANX}") option(ENABLE_DX "Enable the DirectX grabber" ${DEFAULT_DX}) message(STATUS "ENABLE_DX = ${ENABLE_DX}") -if (ENABLE_AMLOGIC) - SET(ENABLE_FB ON) +if(ENABLE_AMLOGIC) + set(ENABLE_FB ON) else() - option(ENABLE_FB " Enable the framebuffer grabber" ${DEFAULT_FB} ) + option(ENABLE_FB " Enable the framebuffer grabber" ${DEFAULT_FB}) endif() message(STATUS "ENABLE_FB = ${ENABLE_FB}") option(ENABLE_MF "Enable the Media Foundation grabber" ${DEFAULT_MF}) message(STATUS "ENABLE_MF = ${ENABLE_MF}") -option(ENABLE_OSX "Enable the OSX grabber" ${DEFAULT_OSX} ) +option(ENABLE_OSX "Enable the OSX grabber" ${DEFAULT_OSX}) message(STATUS "ENABLE_OSX = ${ENABLE_OSX}") option(ENABLE_QT "Enable the Qt grabber" ${DEFAULT_QT}) @@ -287,16 +297,16 @@ removeIndent() message(STATUS "Input options:") addIndent(" - ") -option(ENABLE_BOBLIGHT_SERVER "Enable BOBLIGHT server" ${DEFAULT_BOBLIGHT_SERVER} ) +option(ENABLE_BOBLIGHT_SERVER "Enable BOBLIGHT server" ${DEFAULT_BOBLIGHT_SERVER}) message(STATUS "ENABLE_BOBLIGHT_SERVER = ${ENABLE_BOBLIGHT_SERVER}") -option(ENABLE_CEC "Enable the libcec and CEC control" ${DEFAULT_CEC} ) +option(ENABLE_CEC "Enable the libcec and CEC control" ${DEFAULT_CEC}) message(STATUS "ENABLE_CEC = ${ENABLE_CEC}") -option(ENABLE_FLATBUF_SERVER "Enable Flatbuffers server" ${DEFAULT_FLATBUF_SERVER} ) +option(ENABLE_FLATBUF_SERVER "Enable Flatbuffers server" ${DEFAULT_FLATBUF_SERVER}) message(STATUS "ENABLE_FLATBUF_SERVER = ${ENABLE_FLATBUF_SERVER}") -option(ENABLE_PROTOBUF_SERVER "Enable Protocol Buffers server" ${DEFAULT_PROTOBUF_SERVER} ) +option(ENABLE_PROTOBUF_SERVER "Enable Protocol Buffers server" ${DEFAULT_PROTOBUF_SERVER}) message(STATUS "ENABLE_PROTOBUF_SERVER = ${ENABLE_PROTOBUF_SERVER}") removeIndent() @@ -304,13 +314,13 @@ removeIndent() message(STATUS "Output options:") addIndent(" - ") -option(ENABLE_FORWARDER "Enable Hyperion forwarding" ${DEFAULT_FORWARDER} ) +option(ENABLE_FORWARDER "Enable Hyperion forwarding" ${DEFAULT_FORWARDER}) message(STATUS "ENABLE_FORWARDER = ${ENABLE_FORWARDER}") -if (ENABLE_FORWARDER) - SET(ENABLE_FLATBUF_CONNECT ON) +if(ENABLE_FORWARDER) + set(ENABLE_FLATBUF_CONNECT ON) else() - option(ENABLE_FLATBUF_CONNECT "Enable Flatbuffers connecting remotely" ${DEFAULT_FLATBUF_CONNECT} ) + option(ENABLE_FLATBUF_CONNECT "Enable Flatbuffers connecting remotely" ${DEFAULT_FLATBUF_CONNECT}) endif() message(STATUS "ENABLE_FLATBUF_CONNECT = ${ENABLE_FLATBUF_CONNECT}") @@ -319,22 +329,22 @@ removeIndent() message(STATUS "LED-Device options:") addIndent(" - ") -option(ENABLE_DEV_NETWORK "Enable the Network devices" ${DEFAULT_DEV_NETWORK} ) +option(ENABLE_DEV_NETWORK "Enable the Network devices" ${DEFAULT_DEV_NETWORK}) message(STATUS "ENABLE_DEV_NETWORK = ${ENABLE_DEV_NETWORK}") -option(ENABLE_DEV_SERIAL "Enable the Serial devices" ${DEFAULT_DEV_SERIAL} ) +option(ENABLE_DEV_SERIAL "Enable the Serial devices" ${DEFAULT_DEV_SERIAL}) message(STATUS "ENABLE_DEV_SERIAL = ${ENABLE_DEV_SERIAL}") -option(ENABLE_DEV_SPI "Enable the SPI device" ${DEFAULT_DEV_SPI} ) +option(ENABLE_DEV_SPI "Enable the SPI device" ${DEFAULT_DEV_SPI}) message(STATUS "ENABLE_DEV_SPI = ${ENABLE_DEV_SPI}") option(ENABLE_DEV_TINKERFORGE "Enable the TINKERFORGE device" ${DEFAULT_DEV_TINKERFORGE}) message(STATUS "ENABLE_DEV_TINKERFORGE = ${ENABLE_DEV_TINKERFORGE}") -option(ENABLE_DEV_USB_HID "Enable the libusb and hid devices" ${DEFAULT_DEV_USB_HID} ) +option(ENABLE_DEV_USB_HID "Enable the libusb and hid devices" ${DEFAULT_DEV_USB_HID}) message(STATUS "ENABLE_DEV_USB_HID = ${ENABLE_DEV_USB_HID}") -option(ENABLE_DEV_WS281XPWM "Enable the WS281x-PWM device" ${DEFAULT_DEV_WS281XPWM} ) +option(ENABLE_DEV_WS281XPWM "Enable the WS281x-PWM device" ${DEFAULT_DEV_WS281XPWM}) message(STATUS "ENABLE_DEV_WS281XPWM = ${ENABLE_DEV_WS281XPWM}") removeIndent() @@ -375,7 +385,7 @@ endif() message(STATUS "DEFAULT_USE_SYSTEM_MBEDTLS_LIBS = ${DEFAULT_USE_SYSTEM_MBEDTLS_LIBS}") -if (ENABLE_MDNS) +if(ENABLE_MDNS) message(STATUS "DEFAULT_USE_SYSTEM_QMDNS_LIBS = ${DEFAULT_USE_SYSTEM_QMDNS_LIBS}") endif() @@ -388,14 +398,14 @@ message(STATUS "ENABLE_TESTS = ${ENABLE_TESTS}") removeIndent() -SET ( FLATBUFFERS_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/flatbuf ) -SET ( FLATBUFFERS_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/flatbuf ) +set(FLATBUFFERS_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/flatbuf) +set(FLATBUFFERS_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/flatbuf) -SET ( PROTOBUF_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/proto ) -SET ( PROTOBUF_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/proto ) +set(PROTOBUF_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/proto) +set(PROTOBUF_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/proto) if(ENABLE_JSONCHECKS OR ENABLE_EFFECTENGINE) - if ("${CMAKE_VERSION}" VERSION_LESS "3.12.0") + if("${CMAKE_VERSION}" VERSION_LESS "3.12.0") set(Python_ADDITIONAL_VERSIONS 3.5) find_package(PythonInterp 3.5 REQUIRED) else() @@ -408,38 +418,38 @@ endif() if(ENABLE_JSONCHECKS) # check all json files - FILE ( GLOB_RECURSE HYPERION_SCHEMAS RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/libsrc/*schema*.json ) - SET( JSON_FILES ${CMAKE_BINARY_DIR}/config/hyperion.config.json.default ${HYPERION_SCHEMAS}) + file (GLOB_RECURSE HYPERION_SCHEMAS RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/libsrc/*schema*.json) + set(JSON_FILES ${CMAKE_BINARY_DIR}/config/hyperion.config.json.default ${HYPERION_SCHEMAS}) - EXECUTE_PROCESS ( + execute_process ( COMMAND ${PYTHON_EXECUTABLE} test/jsonchecks/checkjson.py ${JSON_FILES} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} RESULT_VARIABLE CHECK_JSON_FAILED ) - IF ( ${CHECK_JSON_FAILED} ) - MESSAGE (FATAL_ERROR "check of json files failed" ) - ENDIF () + if(${CHECK_JSON_FAILED}) + message (FATAL_ERROR "check of json files failed") + endif() if(ENABLE_EFFECTENGINE) - EXECUTE_PROCESS ( + execute_process ( COMMAND ${PYTHON_EXECUTABLE} test/jsonchecks/checkeffects.py effects effects/schema WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} RESULT_VARIABLE CHECK_EFFECTS_FAILED ) - IF ( ${CHECK_EFFECTS_FAILED} ) - MESSAGE (FATAL_ERROR "check of json effect files failed" ) - ENDIF () + if(${CHECK_EFFECTS_FAILED}) + message (FATAL_ERROR "check of json effect files failed") + endif() endif() - EXECUTE_PROCESS ( + execute_process ( COMMAND ${PYTHON_EXECUTABLE} test/jsonchecks/checkschema.py ${CMAKE_BINARY_DIR}/config/hyperion.config.json.default libsrc/hyperion/hyperion.schema.json WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} RESULT_VARIABLE CHECK_CONFIG_FAILED ) - IF ( ${CHECK_CONFIG_FAILED} ) - MESSAGE (FATAL_ERROR "check of json default config failed" ) - ENDIF () + if(${CHECK_CONFIG_FAILED}) + message (FATAL_ERROR "check of json default config failed") + endif() endif(ENABLE_JSONCHECKS) # Add project specific cmake modules (find, etc) @@ -453,8 +463,8 @@ configure_file("${PROJECT_SOURCE_DIR}/HyperionConfig.h.in" "${PROJECT_BINARY_DIR include_directories("${PROJECT_BINARY_DIR}") # Define the global output path of binaries -SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) -SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) +set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) file(MAKE_DIRECTORY ${LIBRARY_OUTPUT_PATH}) file(MAKE_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) @@ -467,32 +477,21 @@ include_directories(${CMAKE_SOURCE_DIR}/include) #set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so") # MSVC options -if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") +if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") # Search for Windows SDK find_package(WindowsSDK REQUIRED) message(STATUS "WINDOWS SDK: ${WINDOWSSDK_LATEST_DIR} ${WINDOWSSDK_LATEST_NAME}") message(STATUS "MSVC VERSION: ${MSVC_VERSION}") - - # Search for DirectX9 - if (ENABLE_DX) - find_package(DirectX9 REQUIRED) - endif(ENABLE_DX) - -endif() - -# Use GNU gold linker if available -if (NOT WIN32 AND NOT APPLE) - include (${CMAKE_CURRENT_SOURCE_DIR}/cmake/LDGold.cmake) endif() # Don't create new dynamic tags (RUNPATH) and setup -rpath to search for shared libs in BINARY/../lib folder (only for Unix) -if (ENABLE_DEPLOY_DEPENDENCIES AND UNIX AND NOT APPLE) +if(ENABLE_DEPLOY_DEPENDENCIES AND UNIX AND NOT APPLE) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--disable-new-dtags") - SET(CMAKE_SKIP_BUILD_RPATH FALSE) - SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH}:$ORIGIN/../lib") - SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -endif () + set(CMAKE_SKIP_BUILD_RPATH FALSE) + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib") + set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +endif() if(APPLE) set(CMAKE_EXE_LINKER_FLAGS "-framework CoreGraphics") @@ -503,18 +502,18 @@ find_package(Threads REQUIRED) # Allow to overwrite QT base directory # Either supply QTDIR as -DQTDIR= to cmake or set and environment variable QTDIR pointing to the Qt installation # For Windows and OSX, the default Qt installation path are tried to resolved automatically -if (NOT DEFINED QTDIR) - if (DEFINED ENV{QTDIR}) +if(NOT DEFINED QTDIR) + if(DEFINED ENV{QTDIR}) set(QTDIR $ENV{QTDIR}) else() - if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") FIRSTSUBDIR(SUBDIRQT "C:/Qt") - if (NOT ${SUBDIRQT} STREQUAL "") + if(NOT ${SUBDIRQT} STREQUAL "") set(QTDIR "${SUBDIRQT}/msvc2019_64") endif() - elseif ( "${PLATFORM}" MATCHES "osx" ) + elseif ("${PLATFORM}" MATCHES "osx") # QT6 x86_64 location - if (EXISTS /usr/local/opt/qt6) + if(EXISTS /usr/local/opt/qt6) set(QTDIR "/usr/local/opt/qt6") # QT6 arm64 location elseif (EXISTS /opt/homebrew/opt/qt@6) @@ -530,34 +529,34 @@ if (NOT DEFINED QTDIR) endif() endif() -if (DEFINED QTDIR) +if(DEFINED QTDIR) message(STATUS "Add QTDIR: ${QTDIR} to CMAKE_PREFIX_PATH") list(PREPEND CMAKE_PREFIX_PATH ${QTDIR} "${QTDIR}/lib") endif() -if (CMAKE_PREFIX_PATH) - message( STATUS "CMAKE_PREFIX_PATH used: ${CMAKE_PREFIX_PATH}" ) +if(CMAKE_PREFIX_PATH) + message(STATUS "CMAKE_PREFIX_PATH used: ${CMAKE_PREFIX_PATH}") endif() # find QT libs find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network Sql Widgets REQUIRED) -message( STATUS "Found Qt Version: ${QT_VERSION}" ) +message(STATUS "Found Qt Version: ${QT_VERSION}") -if (${QT_VERSION_MAJOR} GREATER_EQUAL 6 ) - SET(QT_MIN_VERSION "6.2.2") +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + set(QT_MIN_VERSION "6.2.2") else() - SET(QT_MIN_VERSION "5.5.0") + set(QT_MIN_VERSION "5.5.0") endif() -if ( "${QT_VERSION}" VERSION_LESS "${QT_MIN_VERSION}" ) - message( FATAL_ERROR "Your Qt version is to old! Minimum required ${QT_MIN_VERSION}" ) +if("${QT_VERSION}" VERSION_LESS "${QT_MIN_VERSION}") + message(FATAL_ERROR "Your Qt version is to old! Minimum required ${QT_MIN_VERSION}") endif() find_package(Qt${QT_VERSION_MAJOR} ${QT_VERSION} COMPONENTS Core Gui Network Sql Widgets REQUIRED) -message( STATUS "Qt version used: ${QT_VERSION}" ) +message(STATUS "Qt version used: ${QT_VERSION}") -if (APPLE AND (${QT_VERSION_MAJOR} GREATER_EQUAL 6) ) +if(APPLE AND (${QT_VERSION_MAJOR} GREATER_EQUAL 6)) set(OPENSSL_ROOT_DIR /usr/local/opt/openssl) endif() @@ -569,29 +568,29 @@ add_definitions(${QT_DEFINITIONS}) add_subdirectory(dependencies) add_subdirectory(libsrc) add_subdirectory(src) -if (ENABLE_TESTS) +if(ENABLE_TESTS) add_subdirectory(test) -endif () +endif() # Add resources directory add_subdirectory(resources) # remove generated files on make cleaan too -LIST( APPEND GENERATED_QRC +list(APPEND GENERATED_QRC ${CMAKE_BINARY_DIR}/WebConfig.qrc ${CMAKE_BINARY_DIR}/HyperionConfig.h ) if(ENABLE_EFFECTENGINE) -LIST( APPEND GENERATED_QRC +list(APPEND GENERATED_QRC ${CMAKE_BINARY_DIR}/EffectEngine.qrc ) -endif () +endif() -set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${GENERATED_QRC}" ) +set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${GENERATED_QRC}") # uninstall target -configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) # enable make package - no code after this line ! diff --git a/bin/scripts/docker-compile.sh b/bin/scripts/docker-compile.sh index ce298b96..bafde87f 100755 --- a/bin/scripts/docker-compile.sh +++ b/bin/scripts/docker-compile.sh @@ -1,4 +1,4 @@ -#!/bin/bash -e +#!/bin/bash DOCKER="docker" # Git repo url of Hyperion @@ -7,20 +7,21 @@ GIT_REPO_URL="https://github.com/hyperion-project/hyperion.ng.git" REGISTRY_URL="ghcr.io/hyperion-project" # cmake build type BUILD_TYPE="Release" -# the docker image at GitHub Container Registry -BUILD_IMAGE="x86_64" -# the docker tag at GitHub Container Registry -BUILD_TAG="bullseye" +DISTRIBUTION="debian" +CODENAME="bullseye" +ARCHITECTURE="amd64" # build packages (.deb .zip ...) BUILD_PACKAGES=true # packages string inserted to cmake cmd PACKAGES="" # platform string inserted to cmake cmd BUILD_PLATFORM="" +#Run build with Qt6 or Qt5 +BUILD_WITH_QT5=false #Run build using GitHub code files -BUILD_LOCAL=0 +BUILD_LOCAL=false #Build from scratch -BUILD_INCREMENTAL=0 +BUILD_INCREMENTAL=false #Verbose output _VERBOSE=0 #Additional args @@ -37,11 +38,7 @@ cd `dirname ${BASE_PATH}` > /dev/null BASE_PATH=`pwd`; popd > /dev/null -BASE_PATH=`pwd`;function log () { - if [[ $_V -eq 1 ]]; then - echo "$@" - fi -} +BASE_PATH=`pwd`; set +e ${DOCKER} ps >/dev/null 2>&1 @@ -62,7 +59,7 @@ function printHelp { echo "######################################################## ## A script to compile Hyperion inside a docker container ## Requires installed Docker: https://www.docker.com/ -## Without arguments it will compile Hyperion for Debian Bullseye (x86_64) and uses Hyperion code from GitHub repository. +## Without arguments it will compile Hyperion for ${DISTRIBUTION}:${CODENAME}, ${ARCHITECTURE} architecture and uses Hyperion code from GitHub repository. ## For all images and tags currently available, see https://github.com/orgs/hyperion-project/packages ## ## Homepage: https://www.hyperion-project.org @@ -71,16 +68,17 @@ echo "######################################################## # These are possible arguments to modify the script behaviour with their default values # # docker-compile.sh -h, --help # Show this help message -# docker-compile.sh -i, --image # The docker image, e.g., x86_64, armv6l, armv7l, aarch64 -# docker-compile.sh -t, --tag # The docker tag, e.g., buster, bullseye, bookworm +# docker-compile.sh -n, --name # The distribution's codename, e.g., buster, bullseye, bookworm, jammy, trixie, lunar, mantic; Note: for Fedora it is the version number +# docker-compile.sh -a, --architecture # The output architecture, e.g., amd64, arm64, arm/v7 # docker-compile.sh -b, --type # Release or Debug build # docker-compile.sh -p, --packages # If true, build packages with CPack +# docker-compile.sh --qt5 # Build with Qt5, otherwise build with Qt6 +# docker-compile.sh -f, --platform # cmake PLATFORM parameter, e.g. x11, amlogic-dev # docker-compile.sh -l, --local # Run build using local code files # docker-compile.sh -c, --incremental # Run incremental build, i.e. do not delete files created during previous build -# docker-compile.sh -f, --platform # cmake PLATFORM parameter, e.g. x11, amlogic-dev # docker-compile.sh -v, --verbose # Run the script in verbose mode # docker-compile.sh -- args # Additonal cmake arguments, e.g., -DHYPERION_LIGHT=ON -# More informations to docker tags at: https://github.com/Hyperion-Project/hyperion.docker-ci" +# More informations to docker containers available at: https://github.com/Hyperion-Project/hyperion.docker-ci" } function log () { @@ -89,48 +87,63 @@ function log () { fi } +function check_distribution () { + url=${REGISTRY_URL}/$1:${CODENAME} + + log "Check for distribution at: $url" + if $($DOCKER buildx imagetools inspect "$url" 2>&1 | grep -q $2) ; then + rc=0 + else + rc=1 + fi + return $rc +} + echo "Compile Hyperion using a Docker container" -options=$(getopt -l "image:,tag:,type:,packages:,platform:,local,incremental,verbose,help" -o "i:t:b:p:f:lcvh" -a -- "$@") +options=$(getopt -l "architecture:,name:,type:,packages:,platform:,qt5,local,incremental,verbose,help" -o "a:n:b:p:f:lcvh" -a -- "$@") eval set -- "$options" while true do case $1 in - -i|--image) + -a|--architecture) shift - BUILD_IMAGE=$1 + ARCHITECTURE=`echo $1 | tr '[:upper:]' '[:lower:]'` ;; - -t|--tag) + -n|--name) shift - BUILD_TAG=$1 + CODENAME=`echo $1 | tr '[:upper:]' '[:lower:]'` ;; - -b|--type) + -b|--type) shift BUILD_TYPE=$1 ;; - -p|--packages) + -p|--packages) shift BUILD_PACKAGES=$1 ;; - -f|--platform) + -f|--platform) shift BUILD_PLATFORM=$1 ;; - -l|--local) - BUILD_LOCAL=1 + --qt5) + BUILD_WITH_QT5=true ;; - -c|--incremental) - BUILD_INCREMENTAL=1 + -l|--local) + BUILD_LOCAL=true ;; - -v|--verbose) + -i|--incremental) + BUILD_INCREMENTAL=true + ;; + -v|--verbose) _VERBOSE=1 ;; - -h|--help) + -h|--help) printHelp exit 0 ;; --) - shift + shift break;; esac shift @@ -148,7 +161,66 @@ if [[ ! -z ${BUILD_PLATFORM} ]]; then PLATFORM="-DPLATFORM=${BUILD_PLATFORM}" fi -echo "---> Initialize with IMAGE:TAG=${BUILD_IMAGE}:${BUILD_TAG}, BUILD_TYPE=${BUILD_TYPE}, BUILD_PACKAGES=${BUILD_PACKAGES}, PLATFORM=${BUILD_PLATFORM}, BUILD_LOCAL=${BUILD_LOCAL}, BUILD_INCREMENTAL=${BUILD_INCREMENTAL}" +PLATFORM_ARCHITECTURE="linux/"${ARCHITECTURE} + +QTVERSION="5" +if [ ${BUILD_WITH_QT5} == false ]; then + QTVERSION="6" + CODENAME="${CODENAME}-qt6" +fi + +echo "---> Evaluate distribution for codename:${CODENAME} on platform architecture ${PLATFORM_ARCHITECTURE}" +DISTRIBUTION="debian" +if ! check_distribution ${DISTRIBUTION} ${PLATFORM_ARCHITECTURE} ; then + DISTRIBUTION="ubuntu" + if ! check_distribution ${DISTRIBUTION} ${PLATFORM_ARCHITECTURE} ; then + DISTRIBUTION="fedora" + if ! check_distribution ${DISTRIBUTION} ${PLATFORM_ARCHITECTURE} ; then + echo "No docker image found for a distribution with codename: ${CODENAME} to be build on platform architecture ${PLATFORM_ARCHITECTURE}" + exit 1 + fi + fi +fi + +echo "---> Build with -> Distribution: ${DISTRIBUTION}, Codename: ${CODENAME}, Architecture: ${ARCHITECTURE}, Type: ${BUILD_TYPE}, Platform: ${BUILD_PLATFORM}, QT Version: ${QTVERSION}, Build Packages: ${BUILD_PACKAGES}, Build local: ${BUILD_LOCAL}, Build incremental: ${BUILD_INCREMENTAL}" + +# Determine the current architecture +CURRENT_ARCHITECTURE=`uname -m` + +#Test if multiarchitecture setup, i.e. user-space is 32bit +if [ ${CURRENT_ARCHITECTURE} == "aarch64" ]; then + CURRENT_ARCHITECTURE="arm64" + USER_ARCHITECTURE=$CURRENT_ARCHITECTURE + IS_V7L=`cat /proc/$$/maps |grep -m1 -c v7l` + if [ $IS_V7L -ne 0 ]; then + USER_ARCHITECTURE="arm/v7" + else + IS_V6L=`cat /proc/$$/maps |grep -m1 -c v6l` + if [ $IS_V6L -ne 0 ]; then + USER_ARCHITECTURE="arm/v6" + fi + fi + if [ $ARCHITECTURE != $USER_ARCHITECTURE ]; then + log "Identified user space current architecture: $USER_ARCHITECTURE" + CURRENT_ARCHITECTURE=$USER_ARCHITECTURE + fi +else + CURRENT_ARCHITECTURE=${CURRENT_ARCHITECTURE//x86_/amd} +fi + +log "Identified kernel current architecture: $CURRENT_ARCHITECTURE" +if [ $ARCHITECTURE != $CURRENT_ARCHITECTURE ]; then + echo "---> Build is not for the same architecturem, enable emulation for ${PLATFORM_ARCHITECTURE}" + ENTRYPOINT_OPTION= + + if [ $CURRENT_ARCHITECTURE != "amd64" ]; then + echo "---> Emulation builds can only be executed on linux/amd64, linux/x86_64 platforms, current architecture is ${CURRENT_ARCHITECTURE}" + exit 1 + fi +else + log "Build natively for platform architecture: ${PLATFORM_ARCHITECTURE}" + ENTRYPOINT_OPTION="--entrypoint=""" +fi log "---> BASE_PATH = ${BASE_PATH}" CODE_PATH=${BASE_PATH}; @@ -163,9 +235,11 @@ git clone --recursive --depth 1 -q ${GIT_REPO_URL} ${CODE_PATH} || { echo "---> fi log "---> CODE_PATH = ${CODE_PATH}" -BUILD_DIR="build-${BUILD_IMAGE}-${BUILD_TAG}" +ARCHITECTURE_PATH=${ARCHITECTURE//\//_} + +BUILD_DIR="build-${CODENAME}-${ARCHITECTURE_PATH}" BUILD_PATH="${CODE_PATH}/${BUILD_DIR}" -DEPLOY_DIR="deploy/${BUILD_IMAGE}/${BUILD_TAG}" +DEPLOY_DIR="deploy/${CODENAME}/${ARCHITECTURE}" DEPLOY_PATH="${CODE_PATH}/${DEPLOY_DIR}" log "---> BUILD_DIR = ${BUILD_DIR}" @@ -178,7 +252,7 @@ sudo rm -fr "${DEPLOY_PATH}" >/dev/null 2>&1 mkdir -p "${DEPLOY_PATH}" >/dev/null 2>&1 #Remove previous build area, if no incremental build -if [ ${BUILD_INCREMENTAL} != 1 ]; then +if [ ${BUILD_INCREMENTAL} != true ]; then sudo rm -fr "${BUILD_PATH}" >/dev/null 2>&1 fi mkdir -p "${BUILD_PATH}" >/dev/null 2>&1 @@ -194,10 +268,11 @@ echo "---> Compiling Hyperion from source code at ${CODE_PATH}" # execute inside container all commands on bash echo "---> Startup docker..." -$DOCKER run --rm \ +$DOCKER run --rm --platform=${PLATFORM_ARCHITECTURE} \ + ${ENTRYPOINT_OPTION} \ -v "${DEPLOY_PATH}:/deploy" \ -v "${CODE_PATH}/:/source:rw" \ - ${REGISTRY_URL}/${BUILD_IMAGE}:${BUILD_TAG} \ + ${REGISTRY_URL}/${DISTRIBUTION}:${CODENAME} \ /bin/bash -c "mkdir -p /source/${BUILD_DIR} && cd /source/${BUILD_DIR} && cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${PLATFORM} ${BUILD_ARGS} .. || exit 2 && make -j $(nproc) ${PACKAGES} || exit 3 || : && @@ -210,7 +285,7 @@ DOCKERRC=${?} sudo chown -fR $(stat -c "%U:%G" ${BASE_PATH}) ${BUILD_PATH} if [ ${DOCKERRC} == 0 ]; then - if [ ${BUILD_LOCAL} == 1 ]; then + if [ ${BUILD_LOCAL} == true ]; then echo "---> Find compiled binaries in: ${BUILD_PATH}/bin" fi diff --git a/bin/scripts/install_pr.sh b/bin/scripts/install_pr.sh index 1f654f23..cd023504 100755 --- a/bin/scripts/install_pr.sh +++ b/bin/scripts/install_pr.sh @@ -73,13 +73,13 @@ if [ ${ARCHITECTURE} == "aarch64" ]; then IS_V7L=`cat /proc/$$/maps |grep -m1 -c v7l` if [ $IS_V7L -ne 0 ]; then USER_ARCHITECTURE="armv7l" - else + else IS_V6L=`cat /proc/$$/maps |grep -m1 -c v6l` if [ $IS_V6L -ne 0 ]; then USER_ARCHITECTURE="armv6l" fi fi - if [ $ARCHITECTURE != $USER_ARCHITECTURE ]; then + if [ $ARCHITECTURE != $USER_ARCHITECTURE ]; then echo "---> Identified kernel target architecture: $ARCHITECTURE" echo "---> Identified user space target architecture: $USER_ARCHITECTURE" ARCHITECTURE=$USER_ARCHITECTURE @@ -134,11 +134,11 @@ if [ -z "$run_id" ]; then # Determine run_id from head_sha runs=$(request_call "$api_url/actions/runs?head_sha=$head_sha") run_id=$(echo "$runs" | tr '\r\n' ' ' | ${pythonCmd} -c """ -import json,sys +import json,sys,os data = json.load(sys.stdin) for i in data['workflow_runs']: - if i['name'] == 'Hyperion PR Build': + if os.path.basename(i['path']) == 'push_pull.yml': print(i['id']) break """ 2>/dev/null) @@ -198,11 +198,11 @@ if [[ ! -z ${CURRENT_SERVICE} ]]; then echo "---> Stop current service: ${CURRENT_SERVICE}" STOPCMD="systemctl stop --quiet ${CURRENT_SERVICE} --now" - USERNAME=${SUDO_USER:-$(whoami)} + USERNAME=${SUDO_USER:-$(whoami)} if [ ${USERNAME} != "root" ]; then STOPCMD="sudo ${STOPCMD}" fi - + ${STOPCMD} >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "---> Critical Error: Failed to stop service: ${CURRENT_SERVICE}, Hyperion may not be started. Stop Hyperion manually." diff --git a/bin/service/hyperion.systemd b/bin/service/hyperion.systemd index adee0eed..7087d6b0 100644 --- a/bin/service/hyperion.systemd +++ b/bin/service/hyperion.systemd @@ -1,5 +1,5 @@ [Unit] -Description=Hyperion ambient light systemd service for user %i +Description=Hyperion ambient light systemd service for user %i Documentation=https://docs.hyperion-project.org Requisite=network.target Wants=network-online.target diff --git a/bin/service/hyperion.xml b/bin/service/hyperion.xml new file mode 100644 index 00000000..1ecc4e7e --- /dev/null +++ b/bin/service/hyperion.xml @@ -0,0 +1,22 @@ + + + Hyperion + Hyperion.NG firewall rules + + + + + + + + + + + + + + + + + + diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 40ada3e1..847c4c40 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -14,7 +14,7 @@ macro(DeployMacOS TARGET) install(CODE "set(PLUGIN_DIR \"${QT_PLUGIN_DIR}\")" COMPONENT "Hyperion") install(CODE "set(BUILD_DIR \"${CMAKE_BINARY_DIR}\")" COMPONENT "Hyperion") install(CODE "set(ENABLE_EFFECTENGINE \"${ENABLE_EFFECTENGINE}\")" COMPONENT "Hyperion") - + install(CODE [[ file(GET_RUNTIME_DEPENDENCIES @@ -36,6 +36,7 @@ macro(DeployMacOS TARGET) FILES "${dependency}" DESTINATION "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/lib" TYPE SHARED_LIBRARY + FOLLOW_SYMLINK_CHAIN ) endif() endforeach() @@ -48,7 +49,6 @@ macro(DeployMacOS TARGET) foreach(PLUGIN "platforms" "sqldrivers" "imageformats") if(EXISTS ${PLUGIN_DIR}/${PLUGIN}) file(GLOB files "${PLUGIN_DIR}/${PLUGIN}/*") - list(FILTER files EXCLUDE REGEX ".*libqwebp\\.dylib$") foreach(file ${files}) file(GET_RUNTIME_DEPENDENCIES EXECUTABLES ${file} @@ -61,6 +61,7 @@ macro(DeployMacOS TARGET) DESTINATION "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/lib" TYPE SHARED_LIBRARY FILES ${DEPENDENCY} + FOLLOW_SYMLINK_CHAIN ) endforeach() @@ -76,25 +77,27 @@ macro(DeployMacOS TARGET) endif() endforeach() - include(BundleUtilities) + include(BundleUtilities) fixup_bundle("${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}" "${QT_PLUGINS}" "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/lib" IGNORE_ITEM "python;python3;Python;Python3;.Python;.Python3") if(ENABLE_EFFECTENGINE) # Detect the Python version and modules directory - find_package(Python3 3.5 REQUIRED) - execute_process( - COMMAND ${Python3_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(standard_lib=True))" - OUTPUT_VARIABLE PYTHON_MODULES_DIR - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + if(NOT CMAKE_VERSION VERSION_LESS "3.12") + find_package(Python3 COMPONENTS Interpreter Development REQUIRED) + set(PYTHON_VERSION_MAJOR_MINOR "${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}") + set(PYTHON_MODULES_DIR ${Python3_STDLIB}) + else() + find_package (PythonLibs ${PYTHON_VERSION_STRING} EXACT) + set(PYTHON_VERSION_MAJOR_MINOR "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") + set(PYTHON_MODULES_DIR ${Python_STDLIB}) + endif() MESSAGE("Add Python ${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR} to bundle") MESSAGE("PYTHON_MODULES_DIR: ${PYTHON_MODULES_DIR}") # Copy Python modules to '/../Frameworks/Python.framework/Versions/Current/lib/PythonMAJOR.MINOR' and ignore the unnecessary stuff listed below if (PYTHON_MODULES_DIR) - set(PYTHON_VERSION_MAJOR_MINOR "${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}") file( COPY ${PYTHON_MODULES_DIR}/ DESTINATION "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/Frameworks/Python.framework/Versions/Current/lib/python${PYTHON_VERSION_MAJOR_MINOR}" @@ -167,9 +170,9 @@ macro(DeployLinux TARGET) # Extract dependencies ignoring the system ones get_prerequisites(${TARGET_FILE} DEPENDENCIES 0 1 "" "") - + message(STATUS "Dependencies for target file: ${DEPENDENCIES}") - + # Append symlink and non-symlink dependencies to the list set(PREREQUISITE_LIBS "") foreach(DEPENDENCY ${DEPENDENCIES}) @@ -276,15 +279,13 @@ macro(DeployLinux TARGET) if(ENABLE_EFFECTENGINE) # Detect the Python version and modules directory if (NOT CMAKE_VERSION VERSION_LESS "3.12") + find_package(Python3 COMPONENTS Interpreter Development REQUIRED) set(PYTHON_VERSION_MAJOR_MINOR "${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}") - set(PYTHON_MODULES_DIR "${Python3_STDLIB}") + set(PYTHON_MODULES_DIR ${Python3_STDLIB}) else() + find_package (PythonLibs ${PYTHON_VERSION_STRING} EXACT) set(PYTHON_VERSION_MAJOR_MINOR "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") - execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(standard_lib=True))" - OUTPUT_VARIABLE PYTHON_MODULES_DIR - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + set(PYTHON_MODULES_DIR ${Python_STDLIB}) endif() # Copy Python modules to 'share/hyperion/lib/pythonMAJOR.MINOR' and ignore the unnecessary stuff listed below @@ -381,19 +382,25 @@ macro(DeployWindows TARGET) list(GET openssl_versions 0 openssl_version_major) list(GET openssl_versions 1 openssl_version_minor) - set(library_suffix "-${openssl_version_major}_${openssl_version_minor}") + set(open_ssl_version_suffix) + if (openssl_version_major VERSION_EQUAL 1 AND openssl_version_minor VERSION_EQUAL 1) + set(open_ssl_version_suffix "-1_1") + else() + set(open_ssl_version_suffix "-3") + endif() + if (CMAKE_SIZEOF_VOID_P EQUAL 8) - string(APPEND library_suffix "-x64") + string(APPEND open_ssl_version_suffix "-x64") endif() find_file(OPENSSL_SSL - NAMES "libssl${library_suffix}.dll" + NAMES "libssl${open_ssl_version_suffix}.dll" PATHS ${OPENSSL_INCLUDE_DIR}/.. ${OPENSSL_INCLUDE_DIR}/../bin NO_DEFAULT_PATH ) find_file(OPENSSL_CRYPTO - NAMES "libcrypto${library_suffix}.dll" + NAMES "libcrypto${open_ssl_version_suffix}.dll" PATHS ${OPENSSL_INCLUDE_DIR}/.. ${OPENSSL_INCLUDE_DIR}/../bin NO_DEFAULT_PATH ) diff --git a/cmake/Findqmdnsengine.cmake b/cmake/Findqmdnsengine.cmake index ceb7a6b8..dac67533 100644 --- a/cmake/Findqmdnsengine.cmake +++ b/cmake/Findqmdnsengine.cmake @@ -19,4 +19,10 @@ find_package_handle_standard_args(qmdnsengine REQUIRED_VARS QMDNS_INCLUDE_DIR QMDNS_LIBRARIES ) -mark_as_advanced(QMDNS_INCLUDE_DIR QMDNS_LIBRARIES) +if(QMDNSENGINE_FOUND) + add_library(qmdnsengine STATIC IMPORTED GLOBAL) + set_target_properties(qmdnsengine PROPERTIES + IMPORTED_LOCATION ${QMDNS_LIBRARIES} + INTERFACE_INCLUDE_DIRECTORIES ${QMDNS_INCLUDE_DIR} + ) +endif() diff --git a/cmake/LDGold.cmake b/cmake/LDGold.cmake deleted file mode 100644 index 92e59694..00000000 --- a/cmake/LDGold.cmake +++ /dev/null @@ -1,17 +0,0 @@ -option(ENABLE_LDGOLD "Use GNU gold linker" ON) - -set(LDGOLD_FOUND FALSE) -if(ENABLE_LDGOLD) - execute_process(COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION) - if(LD_VERSION MATCHES "GNU gold") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold") - set(LDGOLD_FOUND TRUE) - message(STATUS "Linker: GNU gold") - else() - message(STATUS "GNU gold linker is not available, falling back to default system linker") - endif() -else() - message(STATUS "Linker: Default system linker") -endif() diff --git a/cmake/desktop/hyperiond.desktop b/cmake/desktop/hyperion.desktop similarity index 61% rename from cmake/desktop/hyperiond.desktop rename to cmake/desktop/hyperion.desktop index d47a38d3..67d02cbd 100644 --- a/cmake/desktop/hyperiond.desktop +++ b/cmake/desktop/hyperion.desktop @@ -1,8 +1,8 @@ [Desktop Entry] Name=Hyperion GenericName=Hyperion Ambient Lighting -Comment=Hyperion mimics the well known Ambilight from Philips -Icon=/usr/share/pixmaps/hyperion/hyperiond_128.png +Comment=Hyperion is an opensource Bias or Ambient Lighting implementation +Icon=hyperion Terminal=false TryExec=hyperiond Exec=hyperiond diff --git a/cmake/desktop/hyperion.metainfo.xml b/cmake/desktop/hyperion.metainfo.xml new file mode 100644 index 00000000..bc1ac7e6 --- /dev/null +++ b/cmake/desktop/hyperion.metainfo.xml @@ -0,0 +1,48 @@ + + + + com.hyperion-project.hyperion + MIT + MIT + Hyperion + The successor to Hyperion aka Hyperion Next Generation. + + +

+ Hyperion is an opensource Bias or Ambient Lighting implementation which you might know from TV manufacturers. + It supports many LED devices and video grabbers. +

+
+ + https://hyperion-project.org + https://github.com/hyperion-project/hyperion.ng/issues + https://hyperion-project.org + https://docs.hyperion-project.org/ + https://www.paypal.me/HyperionAmbi + https://poeditor.com/join/project/Y4F6vHRFjA + + + + + + + + + + + The multi language web interface + https://raw.githubusercontent.com/hyperion-project/hyperion.ng/master/doc/screenshot.png + + + + + Application + + + com.hyperion-project.hyperion.desktop + + Hyperion Project + admin@hyperion-project.org + + +
diff --git a/cmake/desktop/hyperiond_128.png b/cmake/desktop/hyperiond_128.png deleted file mode 100644 index dc400fc7043854e980d540f9bff4f13ebff772ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16979 zcmeIZXH*p3)-GC222pYlK@bE%M9CQ%MKVYZk~20rXXpk&kSGFzWQmeRl4MjeA|j#5 zNKTS7bU20Ye)qTc8T*XyocrgVamVduRn4mP%vy8K^{h2lRW&a)o+w-;VITnj;Hr}1 zV=ZuxxGY2j;P$JuMLz&Qf?%?;8Zav>0N_jrN{~|gD#O@mW~3rl!s<=#)}tZul$ypZ z=`QW(M=}DVliViRlt@-*Dz2dKtV$R)Lzw{(QK|Jufmcvz=VYe)M#9 zp8oe6q`O2N9;(A?3s)E3s%bAat20`%YjQ~6xjDQZ+7WM@!Wg`0Tr<%xAnVT|q|V4n z8^v_bi_xxrj{WMWbOWiK#X_Lo^y_9Cz=CWe@!V_6LiVqLvx} z_;Y|A6b1n2mmReR0A9QRuwf1W;;8^Y=bF)|DG62(SgI&I2C$dkoR*?Q@C>o5;!_U* zxI%eZAU|hF=#o`Gp#cYpTRG4Rx~@L_Rvcd>;z+OT-~x!SP& z>3`AK0&Sx>##^MQRcf)&y=t*1WP)1T@DO^^gUJcSbc08c{B`|>ifs;RZ7+%~LM?nl z_1K-{+XZhq`Cq?5@{y0V=a$?PybmhVQe*h9stHRgvjn-XUw5Ulq+-(EHI@zy-!+H`-Fb{1y7xJ)EjeIOOccm?Y4%OEk{BaV-8 zi()s_>?m%Es+CwwnzE`*Rim@{MeHv~_MXdj(Zf*Ly*?#w=4Pk($d$d_2WNeU zbpOnpw?i`8f;wG}Gm1BWb!$=fw2zEn_momyn3~iD$6JkyV!oP1KqpQSrq3+TdEjWn z|GT`s!^JKuz$p3s=-c<=(dwi%3U;1{ii*o@+Dkv5YeH8Xrfc@Q!wekHw^9S7({R&F zginMOa;5xq>fh3-N8eI>8$!+hT-*6UDp^oq5R;E!I&xF^T zHu(3F+T-PnZE0^aN1rK3LLJU$@6dKF>qxwL)?2rsat%aRuH?%{MTWg%eQYWtxf`l+ zn|crU*#717OPZSq$z8-g&O+gvNE$g#;+IU5aWk2%Cle{PB`c?Xsnz!7wS&@A9C3qh zriJR1bxZYhw`#{uSD{^u9^d#Z669!~TK^wx&YqA5af`sO*PPqlN8NTMnehPU0Z-zh zN~xvR9m1^U`LC4}u#8ehTS6vS6&;)Nf~St3N30u@?jrbxvPKpGr*Qe7!j) z+5RB}C#L=E5W{|qP$@Cn3dExsCv|Nb**x)r0#7waI@oW|&rO8lo*1n~oTK{Ju_UW| zy8><1U)>XG82@l%m^Y$T1f(GeR9VuF_A;P*XY--M_uQ6K~8Zr+aLP%7oXp~eGb9r=A?ewIgU9R z#)wF1VF+Jn^oCe(%8jaotKOqaYaf#jh|-&n{A4yBu(4}=5wpM#cEa_U{lYbGiHe3c z<36gdM5R1D@rFiEs^@ceH+c|Ov08M{T0G`RAb)5Xy*5=VPpSqP`cAA#&cup%_AB!E zn-bM9de}kam7K(mH}-Y`*Hn4KIf#VDAs5;6C>wLUjA^NNzbbIIZ`MRdMjW?y=^7 z8LGHcSmqzE)G^yoQezC)8(j_1`Z!XdUae)Nw;ShWduRovzqV9oOUgN@Yp}#l~LwYp&zh`sW!fSaF(!e-km@ zYIuD2+V32qSWR9IYKG{l^id6W#R5*{{NGwL@sc-P)K&Iij>y&J?-D6{`xo;Km5hSP zvOw82qYG)x6{*cM3MMJ0@US%^e=Uifi46|bh_^`@rS7^AP57v4q*Oss3vK2HyJOzl zgfC^%gxuSKVng+vdQ_LrEVAj_&FF7^8HO!)fiOE--f?#WyicDNKHeoSd9=)%7@D`m z7hA14a_|^m);%@y_!L1&-szrWNfd1vN#6Oeic`4YX%?lerzPj-XXBRgi7M^%cfSg! zROLH2O->Ja*D@gK?1C4iRztd5~z5 zsqpRb1n%>6%kjUl0Vp|ZO3gsUY#zuE`T$$ANtSsFizp(t5h z;uGYCe-M08I>=y4R292hP%uC*mM7R_S$#h}uV2?EA+Fh^WufIk3vRnr#8T=>o!69J zHI$k0R-CJZK8>UkcWB&W7YDf?R2#+*HA5_xTlBIWHb?VE`Y5Y^vj6tKS^U(xS9P_v zoALKC6;*2HTbVHLn4FoZfgb%PQ&^A!`ZbwB&QCu9;;8p((;+c7B8@J6HSS(3yx#6CG%r| zgM~wz?=9pt+sj?gC--B`WG4@Bk0C!0S`E$oUmkC;IOv_JQVPIS>w;H}UYt~Zu6~EPm4#CxQ3Db>gW#`{k*ZLRld%%Gnx}?W0=(~IcB0GUyDQ;%gXKPnF8c4R=fECy?svV7Unx0xsAAj06rD{D_O$TJcP=@)kj|3mX3uMG5(TxFe1O2 z>#+o1&E4#q`5FZqT|8)WtCO$tvo2XKTaNT6C3R^LIjL9hLDnI(YvPeNf08Uv_c6KJ zAbwGcvJb?3ASnN!$Iq=h{NwsjPOxpLhKNLno_6!?!C)Ep_jRaq?gHCqKGN+KN_qs( z>ShVT-A11stZ-PFK9z~x8EvlUA+HckcO5a@B1QD#bB!jH?g$ZZzUjB7d6@7-{{K6FS;}zal4?3^x-SX8Vy9l5n9YY!WCjW^W<|Nz?xnlQG>1FHMmv|{^ zpuJee4~eU&@T<;k@>{#fZ;7LfI6j47Oh6pEA^Sq{yhj15sBD?w5*@hk9G=zYeyl%- zvTHjgYdbAn#cs#Sh=o$;Oy=o@2~N(wNvdnV``D~3BO`H!S@MBY53o6QRr^K7TSQF- zX%1D7&#lZy;vt@)Vx1ZL9@*H|!>XN&AGs(O?CFel(GGmU1^$~ET^umO+(xJD!@9hv zcqW>pzWHW5DJed)hTo57!(Z!(tTIgJ3EJD292g~T7U@)`^t`TRcAcf1h54a0%t(NE zoqFF5H8K}u$b8mANV_a4?iU!ev!p$Dq|HpSJgm6>gp)bqr)It`{@Ws+ zScpF-4`xSKJMaogfzWzX9+J@ z^lH3M=}YEo?qJf{}eGt%n73nA2`3#w8>LAyR~jk{cAUg?Y7` z{6b?F%+LqW6D0IV1hap1|BbFY@r|&1Fvo6QA<@s(piB3;ePL4te&R44BL3{Kvxyl>361 z?r6|y=^H^yj~hic$i-CUm5sjIG9e+qh(0}7^J4cNt9>&^Sy5w-N0p;+^Nx3UY;jqQQk<@-uPnAk|R>P z+{FLLZuHg(tCr$MTw{b6?hcWB?X3`kFfOWR`^0PQJd|^}`y!{nZ%XG1=jdrtgm4Ad z-1wEAAd&*(ElIBjUKJcHR_>9%^R3wvqPv2Fn1I4}@X{`TgNpY8r=b>;W+ajM6*e?c ze}jxHg|Cv5GrUI=*19c+4|odG+^nUoDRr~Z-*z1e~Rf&|dr+PF@AUO}?XwhyU9 zU#pf_c^S&MULpFBv(yTaf!QKR!tUFXcw9?*kKVDqgHPP(o=Ln`_-?Ys4yT0e8UJHv zzac7t{BVdf&5fv$f>*C^5Rp#AQlu>+8D*?=4!DlL@CE>i_8bVQ&J`y5<`jl)VUe7z zmKfaWD17pyk$z>alIJNguhr@p$Tc2uK08ayOxoAX9P=bXRCGSeiNnufqEGT}ZPz7N zov@CUw8t9}ZC6gne|qLl!hOqK2=e+}by7Qt>=Vjm2yoa$OnZ?Cz4hgc4E5^m^#w9G|DSXoRD924iur7nQZ*Sptq{)0(!|FLAa+Z?s z>4ulu5;So16>q$U;8?K7g!9MH=|fzVGZ}S{QNYX6;V*$d&J%55koNKrD*~B*Z?X}? z#&lxW0Ck*Oqu28~s6a4|vFcU$$IXDmA`Ub2C%<*lpH`(JjE*4w8d~q4=s4Qua10A; z2+EtM86;k}=SM#0kQph*&wxe5HZFL4LN@`r&w+?Z*Uj~ z9nwh0nJD{)sPP@+GK=4yBqk%D>FZbEnA(J80gD_h0SQZD}2QMf}J7I=v{k`ORf2&HPR19=%an=n3_`v4ObO+mY1VmROX$l4J?C(A#q?v3keXn_i>z=#O?;nc|YeSOW<0aH< zeS7(}hrCojD3TghC<*VIr716uvYMnt-SpjqI1l6fn2Nwp3~I-nJt4lS${S>E*&|B# zQ;0)uYpY040=8;W)2Dw`)UeP$sHr&+j_dl#=St@eN!EbcOhCZd2L&G<;PH?7xBT|6 zV3p^SsNDwnSxX$}w@l(I8w#T=e1I)W8TsUnD(mqeB$ExNM_Z0eW2ZU^Kj~guvlrrP z*jhHUan|ykCG7rA8B=)^hb8h2RfcW4^a5IgJ+iKlwCtpUrzup-zI$~3QUf*AXF!I7Fim8OHhmM_( zMkQG0X7(2+oYeyFd)`MIJ#*jxGG_3UCAnc)9bd?ySo;CTFYiEvxapMI+h2S>eK|UF zlIp7~1>_qQ9!#hyT$#bBs!oW4GM`_@`?*gBwfPMNAGpdfXzOdV4N_F(UY5JY{f)Y{ zx)Zn^6&pO>nULe>5rA*Hmx+C-YKhpAvx>pvoL*9D2Y-{NIf5P^I2XqnL%Lx4v%UnM zYXIfy6<8ES{w7<93r_ryJyKTGB9C(HS!!Q0UH$02!p*`_K3bDNiR8dUid6a>O}GuB z@zqFyPb7I5D}hE7FGx*v(q$TJnaH44fL z?(&x0_WxacG%Io1P8bLv`sf_Q>#k{i<2F?7t2~AaGUVy1JQxtX2gX_&p>|Eth7d7! zKIFOvh#QLVnxoA*Ph$)T0&h2{+Vm&0+AsG$GAXxA1vJa}1o=LTbQD)i&^DM-l2r`( zK3~#;@YZ^F!DyPiCCeQqWL~vEJ8JZ~MXTR3oj0xk;6tBBq=glFn#j`P;D^Pf`b+7m z2nUK@m{K>5N^Fix;he~WpUaWywh@hYp|KKub{TYH$qF5x4#*-)pUM&f@8|Ow@oBV7 z8t&#-@gOGN@C}&@)X|iFlyi#}GUKKg(vH3<{oNUVq|+Kqpjl~!K|YLKdFgC;er14b zL6DyesmCVWS5wCJYBJYMDg*m|pMk4oS#1`i4x1h1UIS@Pibm$pxlvvf6<;h3MaDKW*eXYf@ zXy~!iy7G<1atorLW4=^B`?N}B&Rmt-0#3Fsq)!?K(G!b{iy{}TZHl$qnJF9l$h3Y~ zcDI|cnW?Gt@we*1;FDMm8Q7KLyR);b!T~Jfb*Am6#yOu>@Ju1?Vj(>lRo9Fs>17z4 zu0EN3F=1fuFjmm77bp1iwzD~c41W4`P#TLm7`(rMrgi)x%b+rFI4FB`I`KPc2cj08 z6TnYaP=^^pc0o+-7Vj7!`LNV#XMB%atgJk*KXa&#&5tebZ~4i#d~$$KdxjfHsc58l z;|KdG&NxD3fNY$RA2G8}coPpnOyF*G^DQTDnxK417Sz?klOoq8ntVwZm9KTok?~-^ zCqMOP5**Sf1AA^MYs5v`3@bE$uhNr@NPO;&hDC%qZQkP`D~M)Q{w&KkMLxx9660!` zKk&?i)!N>a7{<0@N4nv)$GhaY0U zdA&{NdGqtcsk{FL6tg&oc|hk9e?m7ZNx2-~<|~XE%yz>ttdNQlet)$s+h}(EmXnN8 z&yBSgC4o`^;yERsk=`M7z*>}ELztyQ0nf4DLlbUUc{OI_Gd=9!2Y{xq6J{-Mc zcK*5Tq;-`U`wIJPyCzFT`?nk-h&{$_*`SpJXCuNGK2u!*$9SCtQq8AENRgOfF`qGM z4%ax029U;0sb~uZv&>D+zMgcS9d!1f={XWx{BzT~uAO}aXS4Yu#4b|cN1?MLH+iA+ zuXn~K{9UdVDOJ>s5O9t-jr+_+nx{xQb;v_Y}t0`hwvNqJd5yXsi!<`-f z$igc;DSDJG&9u{e+tK}4jI1bC2ynfmEXur>?tv>5KEp;w^ zOvj9>%({|~J3=ot`3rX_%{!W-PengUN2J)8*jfnu32X?`%bNsWjhokn-A}mBK$T%} zpXj@iv5vy4s}V=oP_3JcS40;gd5l!)-VSL>%I}+NMZIA&tqv75bd2QyGVmG0H$&xU zOJz!BGRqlwlH5hrynZKY(!JVLM)u`|4IBUHsWlE#aZ6TJZn9EicPKn5Ii6(wQENNf zy!&Y-_aY9j06y;$>l3_xCq8@Mc;kD(fI8{~TNSieiGJKHe~A0ULS!<7HaiPPSP0(g z+!S7%hwjj%T5$*>NPmW`4baKnJadF0smq26f6-2*l%VSr@Ssw99*Gef?<9=s^rxi_lBOia zKYezrFJITBCAY#@Q@{FsUtHaFEzPu48Jq9UNgM4)o*9$5J<8tPFU4^SoU)Q<_;prw zm+r7%>6$fWlsY>6;E(wl&kT(@Yug_cSln|GZD?n3z4mCf{DyjYBYF9b@!*t~(E3@k ztnRv;li0Idv@ZQ`Oq9ctE*WtiMW2FmO3+u5TUX@2(r`epdW^|El!EcjV=qvc)I) zIU40gEdkLcLC2Iqfq(ei$+e{$m!4uW~L8;#%E9wp%7d#@vX z(LnrX!Ie!xaTqE0l;hVXA-t$jZDet8oqNuGjx-PJFtKBQ3cbb9&T}qujq=5ZS_Pry0#mzakv6Gk1 zWi+XdkD4ZUCB!2vwHcQhGPj<$CmP=99hTA5HM=&;Y_x#huIR&gXkYm#Jc(oopcK%s zSE+303gCf{A3!*OQ^PwNkrIJCvkS6#C*&@En`+0)ZbW3oY!ci`p7)iXa9L>aaZxI9 z^m5^fg)x5Dk$8aARrt8kqv@<2Yvd5e=xe`+r<{DDVX|}0(*Ec+hxhy93VSO3q6Q7{ zR`Kq89FJN2%2__M*+cYt>>{N}eM7sMf4Jy5iL~DppDFQ+LbcZHpB7gh+Lw#>#_%95 zAMiydq{Y1DZ745G;`JK3mbh$hYW?V;rcG!lakBk=onx^T^PS`DyU%qnyrUsyxSl+8 zyClH(LFTb5EaG+Re60+t&2$Gh3DfDhNE)fF?#9T;8);fA^S{yPJDeQR>L7l7H-5R$ z99PNUZuzeDfHnQW8z+YGLLONLdO1YkpWVT(IdS)v79Q84@pR!XmmC&u6@JhL0QW zJo_IxExIJ3Awxj!_>RqCdhTkuP+6-&X4OVS&+`3r*TS;rH zy^2y}FU*Yyi){!@_H9qDRjynFwgg0^=+iYfOuA3t8_C^hojiJ>U;G5RnOk#S)AaZR zmd#n~^CQ(q*|jRPf42D}No!rQv_^$qpE%%?=m7RatgUv5__6KCo?0>6f8{jJunTZe{7Vb0lti{GJtI)xzNZ)ABXm8#!^e9lo<}j3hKH zaiJNQ#ZHytMcHa6UKCPka}^&7o4v%QnJ%;)x$NFhuxkz$bjNyHH#;ORPMVG=|73n6 zVBzxd;(17Rgsm!3ZuR1|n3Jx(#5*F_J|wxZWBV3w1|KOUNhnPb-gPjp&;9Z>^ic#- z&AERq<3TSvNUvzebw|o*_5{T~hkb5@S;{8M3jXfc+<$cv$LF~Hm5H`PW6R@EqH$vZ zg;h+D?>P)EuvekLK5Q6vr#%2s1l@b7E_pEwMGrH>w;NV5ueNmi*-*F_j&<5x1G6}XGjTt?F&tEa~-Lr!q z+re-6UiYQ>G&f>$dM)`S3o|Z;ch@d>o-TNPCp$BxwMN6T*#OY*Z!69C|vgTkv|L#Qda zbk1r0tM(`FY7x(-ndsS|ZiqQEp8lKMApIxwGh|b7gnQ>Z8dR>CvY%8Le0e>adRU|H zef;0wVB7Ywz~<2q%Q8a-6aa1**cd3;DkH5MY7)i;M%o1#rQ0 zFTg?x%9ohS!T`blvyTDg8~>1#K$-I&7#vXMgWv#H!F>i;_(7Q(+?Rj_7V%eGt)Tn^ zEJT04ilB6P<-hj|N@^M`{P+2I@ALD6`n>#n;=DrQe1a@|V&Vc~;{1Xj8j|y07J;gl zzWl@X%S-+3$EEzYAHtxF`wts0e{DqYFEY(P1j$?lT z#Kgp;S4iovUZv;1#dwSV|M-J# z2PlXxZNi1H0yq>9TnY%b17HU2#E1O({N>7}qqunZ1cXGyBv(K{E%{}naB=W(@$vES zz}g_N9>Al(r@Y1Ykbp|lf{@jXnm;7r9TD53s?Ri9L%X*HEZswiNoeWluQRZ7+~MT9 zD<~u^A}S^>`&dq1K~YIrTSr$<-{7gCm9>qn-7}cIho_gfkFTHq^RVz25s@#WUMD6c zze!1bo0glGUr<=|zPRLLbxmzu{ilY;FJC)4ySjUN`+g3OjE*74Cno0?7MGS+R@c@y z_Vy1BkB(1H&(1IXy7cq!&tIPXPkvE=e&OKZ;o=cq`US!9xpbTY5C0Y)0p&wYLJK!4 zR{jto>PHFhsy-95325!oSh^39(B2lDXWzSY?T=^wdya+vKl1FKj{TQklK?3$1nfLq z3IGb6VWK#5aQj5SRzRw1aRU(Kjno_Nnb*Yp4e(hMQY8ol=jce-h zpsgfxjFZO`F8{WDv$vl$O|$AY{4;dJ>JaE9w3_9?_*lHD?9Oa~Fi|C{AE$%IMuB-210fhTtcoAKJ?g{*}Btz6Wv2erfs0J5G?uaV~ zWd2=G(QV6fi&y~YGM4D8Ek1>RmUhc+&5P~#Dov1&xPHCD&rRh1VcWjTDdD}3c1)|6 z0FB(%rnp(QQGwI6kr(nL)Qk`V^=LouJS>n1KQ0p7$Cwmc!gzCAC*<#xtG-jW@7zmn zTkgiFVS&k1Np2JMNY2VnB$Wk%A?qq=4=hl`d?tbg>}B6M7j+2gPpQhRYu1F36m;=m zf&O?@Iu@wCHLOy+r$%$E!g_7=O1$!@n++CN9{h8`jBPCwXyvEV{iyT3nF!hVFQsn_ zn7hwOhTLS~`>R-h0}E`a&geD5&D{1)XpZE1sgn6o7yjOz8fjy+-TlMUV?zrPJ1T#G z2xfyQ!|lKUxu6>y+0GqRs)^h1u6M%1Ur(XhM)%OrGoMo|&@SNnsUl*KTK|U&%vq;0 z1&PixAZ%~FA$89U3piP0frU~7yQH~pf)*hxa4X22N&QA{izTQhE;#mD0Y*2mB043! z8q^$vDri__ZJsk4hE^T<{g&L3=$ks&n#y=D#UJF*b|pUVrPHI6-vh<^;bvhC^U>V1eEB-R0z$uzQFv=`s`gYbXueRd{^D3&^X%c1(*DFM|PUBbeFMX)N2y=y#e7Q zh9$cnR9wYBMs0+COr5G5XYt+kHhOg^ZBMqT*~=Apca_J#W}SQFwP{5s{eVg2=*aGi zM)7ehu%rX}Q8E%tJC;(Of{|0-E3=CzMOAJ~ZlpF#V1bl`iy>1g{dvJtlvwGty6iIw zXjesN&W2TaJ?635UWIt7;am@9#c$M&+H}~=pGfW5BYV*rCmFALEYOWa+5a{)S=%JU zNVhE-3-Nkkfduro1N*T;%+HJ7Tp@1(jqakExGMPKSCsvu<4gW0Y zTynbDm?48kRz9s@n|DFI9K!;;576{SQ^?vEp|R{%`;Nyg%Ad|NwvW9{BRMPR^iLb+ znaFh0)kcJvIX0M@hI^8$l1asg6dp;8J~%Zs#lr$Q-53>hEYP*-(Ia&;>LC8#&#qyC z@a13X=cMqZ8Z3ZvM*RPne~a20l(Ct3N`DaDbQ!}n+@Z}I9fp#I<=dnNte671;th%-a$}!M3SAqpz zW;39`*uBmygHJJ|DU&<(9rj!>%HCjj9N~w``ZNz$@u@Xs{=35Lb?SqW(VGK1fM_hH zZDYXB_ur52hqZ<6p=;9ZO=_AP`i>QrwaR4c8eAfy6hd9lP#-LiN7;z3?F*FGk|cY7 zbFK*t$V)@*f}OlTe3gau2RjM}3hBNqc3X4DgIK_}qOJjo=h=n@>fjebUbTmwT(fC* zhpS{wHh%&^Mg1u^W(?DF>=*BPY6UVQ0}2{;b+e}8!F)U_%JolJL@ItZmqZOad_$Fj zxiz@-4>$hJb(6}@wll8(;>yF-@c+|LBxP?5*#1jsE|2lr!uHL%yoV75-83m5p$ti> z1EAxI^t<=p{o($hy}n7*QY$;z={py2y^)QHRdy?~^QgY((=~(zsNsTs@uBK;;28N6 ziF%=)Q_jV|<5DSfD9ULXV^+BW*Ra;EGgI@&0_&>Y&jv&U!;qGR{&DZNv#)@J*N(t| z)XKZrWVj$r2k$8$LxE@IrM!<0Qp4>(nmVEe`F*-{gB?$i3v;NXEh`L|#@CW6?lzf4 z!}qmo<1fId=4yQ^=~y7)%ticnMS6302Zbna0|z8UITv&^ZQx&zrRQ@}cC&Q2#ur{` zmsSl!X&qJd+4cl5k3n8uay?J031STIn3W_8`YS$`dL9w?j3nIKPBx$m@hg5lq2Hxv z{+>2GjI*DAzq*RTy`)LLP{5g|%ZunE)C8I@aiosm3i80JcOg;m+fOQ!4Z^^Zti9dp zY@)AN00$&oyh*3)97)@f#^1q7J7YAg^*gWr#rOR*Eq>rX3>Ale9V*7FoSJgfz<*O9 zCuR}wC48qpZT^UbDV&&BGO}HVH&jm5d6>Ay^KmjwheJ;D%<7|onb@B)%)~#y_^mz;D zPugZmQE^mX2${hq5%Axm!>dutk{qr`vp*}om_5^0y~^R} zuwkW2wa1%YWWlG};0W^kYi^xJW(Km3m}h@2w=Tcf40NHFboL|KS~rb0ql=4<9{pj-cn4MEnRYUnb9$ zqn5<9=7v_s64Q>u6{Fufp z>H<~b@tNuKkj#2;6^@_v55Hq zT=eyVsj6$U_hlhpahyshM!gdYyzRZmLvsul7>n;#>PEF=fqh5#UL)*uOB4&FGGo-@ zF(Rh&YVwA4aS8}5kPVLp?alIpl_zX=FLo?q%+1b(p{FyqPLD3}WSA#pAYQsF&&o*Q zR{tET31xPcQ5~b#UBw(<9e)O1Vd)px7oNj=7&kDYWT*^glykFVTFltcJgOE<;BA*C z^@}*g+d^wd7cn;A;`0*H7;Sdb_xth0eI685>z(gon9(phLMG&7t7F0k?%!sFF}{Z1 z;9!n>SL^$9g$_66eZXC=`kDU21akMp>80+NW{?4oL@>HJ;N6u&M$75^dl>TL(^+4S zzAQ;+^?GhA7K)yDCZbA*%Ga&8>ZDoh9oCoOYxxz%Y4nm_KFa0v(*I@z3(#TgG9`6ws-=>p z1)pGn0|KxUz|_^Dv%VK>3r0*GqZ*H4Z4SLtx+mqXe!IQ=-D%r_Xo=MxH!S!HD&9an z>OchbWe?;U*xiw(#Czi4ET8HP-TDN2!rlMQzl{pXxSzo~$$Z%{FA5!EjMR6{mi(0i z<=(a}e`P)%hwlmTpPqwlr941IK~K{TPSYy7M*Ah}N$cVlu)w$Z;>^XdQ7Aq}!M#-k z)Il;I{{VGVG?C6id{r-cut3A-!<~5h)jT2CRm%^au>Lf@kps7Vp0;>m)+(fe}A zLM-ofMoI^ToNnHJwXts&n|%OUTOW@uGOaaL+ACW=)LdT$)3f+e7aYgz!MEd24jB2Q zwyhmBdzQY78&BD$AkXEa7|2%K5!i{u`e-qbC}x;ik0kwM1scPM-DRtzNzuj;s#vk7 z(9PlrxHByKU=9l~y6om-ribCHi+|(_@u)*upD2^o#qyO}vk9~Kv&?`^f7grZRSYmU zuZo%aup@#IpPM(%q$%@#PMO?bbpkyQcw?d4GyyjgC=(wD!2)lv!0949Trb-w#P?7_y+g(uaWq)%2N_*`EQsd;;Dnpl7;?HloHdGT& zewzIBFc!K+@@a4!}!qQC@BSps?19P>63Riyhz?1Wl z(XInyE1y-{Yh|;)&!{jo$hfvOV({UP27FH^d$%GGG($!mmG&G9L{(OD7FIig4>wX` zA~*(L!RL)mH!+CpGZ8{8K%;~ONT#}>hnEBX6ZEv?91~T!72|?JS=YSXTgtvF>8c?9 z0HY?01v>Rdv*=!^z^_eq-re$=OLzs3Ej5i=>aZ-eWbm?aRpJS4wbox6wyrs_F2k~?mbNF9YYgLA#0^2T$^Ow<$Q7LJbvj^6a z>~ziQ$tho?q0IlRe0pfc2>T$^ITrhoePhlZanfzIW2U1ymUp2*g<6s%xV}TRWCC5l zyW4JDF|_$QD2TREu{mh|1HqO8A?6bppN_HY?rg~9lmf^$sTfnE?9&U0mvfjB!HsFF ztmm7ighmR&9qe{?8-7Qh;KaIfy5J4*fWY+PY52TF#zrbgZa?^G+Q)2)z&r8j`rYUz z74VwKkRhd&XXN=a7njWbXQ1}Y_DXnn%k*0{kNiG47k z(;SS_knxqAKiFKcJypQLjIP3}yQB_cvLUdd$$(%+&zdYWnWkCmxAs`iZBB zhSIl?+g|d|djx)?XVK%6@BDxH`|Ih_4C+&8RlFWQaz~{TBLYXXS86L}%S|!a2u(Sf zkd1#X$Q@4uI{lI1&bPDWS}yLrw({FgI6%l;v||BZj9082-~-=nkPN^A55eUuNVdp4 z4Bef0_?iT0@hkWq%@<7>ra9Do^_w7@!J83w7Ld)H;PZms#kuoq;JD3aM#;G*X%K>S zR}#D14nfxUDnd`Ap!4p&Cz$8pZ30MA3S)L^$P5F z!O6uMX6yIARv;ZbeF!QrT()523$qn}1oQOJuyNP6aB_9D0YvYyT>epRN#6lc6#qoY zx;VPHYr9%l*#LZG={QPY<*k2KJ_b$HfjRwU=Ku7v?ZX;Z83?BOCs+<_RNdCr!^RWf z6XR8lJGlH`aq53oD>+%%*{Iq$+j%|%`1nO1dVa0_H}yaD`Y`Lis$UZgi-Ohv<&CzB zt*4KLyAANKZvoeCu5N><+y89D^O={ErLzUh@jrXda^p}N#54W}ysV3ptGkVd2h7D8 z5I(bU)dz7m{{!w{-QX7z)^O#$d^yPUze4`y5D1JUIKO<`$@w3E${KPCO3t1(?pDt% zob7DnUEG~4JOKedVI!?^2M!OT>ac(cF#Nk z4KGVam=()o7bgptb6omHS+E9Bl6&&F;*oji{{ZQx B0#N_} diff --git a/cmake/package-scripts/postinst b/cmake/package-scripts/postinst index 7bc63322..54cd3901 100644 --- a/cmake/package-scripts/postinst +++ b/cmake/package-scripts/postinst @@ -105,9 +105,9 @@ ln -fs $BINSP/scripts/updateHyperionUser.sh $BINTP/updateHyperionUser 2>/dev/nul if [ "$IS_UPGRADE" = false ]; then if hash desktop-file-install 2>/dev/null; then echo "---> Install Hyperion desktop icons" - mkdir /usr/share/pixmaps/hyperion 2>/dev/null - cp /usr/share/hyperion/desktop/*.png /usr/share/pixmaps/hyperion 2>/dev/null - desktop-file-install /usr/share/hyperion/desktop/hyperiond.desktop 2>/dev/null + cp -R /usr/share/hyperion/icons /usr/share/icons/hicolor 2>/dev/null + cp /usr/share/hyperion/desktop/hyperion.metainfo.xml /usr/share/metainfo/hyperion.metainfo.xml 2>/dev/null + desktop-file-install /usr/share/hyperion/desktop/hyperion.desktop 2>/dev/null fi fi @@ -149,7 +149,7 @@ $REBOOTMESSAGE echo "-----------------------------------------------------------------------------" echo "Webpage: www.hyperion-project.org" echo "Forum: www.hyperion-project.org" -echo "Documenation: docs.hyperion-project.org" +echo "Documentation: docs.hyperion-project.org" echo "-----------------------------------------------------------------------------" diff --git a/cmake/package-scripts/prerm b/cmake/package-scripts/prerm index 648c9a2d..ea2b9d66 100644 --- a/cmake/package-scripts/prerm +++ b/cmake/package-scripts/prerm @@ -59,11 +59,29 @@ fi # In case we don't use a service kill all instances killall hyperiond 2> /dev/null -# delete desktop icons; desktop-file-edit is a workaround to hide the entry and delete it afterwards manual. -# TODO Better way for deletion and keep the desktop in sync without logout/login or desktop dependend cmds? -echo "---> Delete Hyperion desktop icons" -desktop-file-edit --set-key=NoDisplay --set-value=true /usr/share/applications/hyperiond.desktop 2> /dev/null +# remove desktop/appstream file rm -v /usr/share/applications/hyperion* 2> /dev/null -rm -rv /usr/share/pixmaps/hyperion 2> /dev/null +rm -v /usr/share/metainfo/hyperion* 2> /dev/null + +# update desktop-database (if exists) +if [ -x /usr/bin/update-desktop-database ]; then + update-desktop-database -q /usr/share/applications +fi + +# remove Hyperion icons +for i in 16x16 22x22 24x24 32x32 36x36 48x48 64x64 72x72 96x96 128x128 192x192 256x256 512x512 + rm -v usr/share/icons/hicolor/$i/apps/hyperion.png 2> /dev/null +done + +# update icon-cache +if [ -e /usr/share/icons/hicolor/icon-theme.cache ] ; then + # touch it, just in case we cannot find the binary... + touch --no-create /usr/share/icons/hicolor + if hash gtk-update-icon-cache 2>/dev/null; then + gtk-update-icon-cache /usr/share/icons/hicolor + fi + # ignore errors + true +fi exit 0 diff --git a/cmake/packages.cmake b/cmake/packages.cmake index 046cefe7..a6701545 100644 --- a/cmake/packages.cmake +++ b/cmake/packages.cmake @@ -52,7 +52,7 @@ SET ( CPACK_PACKAGE_CONTACT "packages@hyperion-project.org") SET ( CPACK_PACKAGE_VENDOR "hyperion-project") SET ( CPACK_PACKAGE_EXECUTABLES "hyperiond;Hyperion" ) SET ( CPACK_PACKAGE_INSTALL_DIRECTORY "Hyperion" ) -SET ( CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/icons/hyperion-icon-32px.png" ) +SET ( CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/icons/hyperion-32px.png" ) SET ( CPACK_PACKAGE_VERSION_MAJOR "${HYPERION_VERSION_MAJOR}") SET ( CPACK_PACKAGE_VERSION_MINOR "${HYPERION_VERSION_MINOR}") diff --git a/cmake/win/win_rc.cmake b/cmake/win/win_rc.cmake deleted file mode 100644 index 203e3794..00000000 --- a/cmake/win/win_rc.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# process a .rc file for windows -# Provides (BINARY_NAME)_WIN_RC_PATH with path to generated file -function(generate_win_rc_file BINARY_NAME) - # target path to store generated files - set(TARGET_PATH ${CMAKE_BINARY_DIR}/win_rc_file/${BINARY_NAME}) - # assets - string(REPLACE "/" "\\\\" WIN_RC_ICON_PATH ${CMAKE_SOURCE_DIR}/cmake/nsis/installer.ico) - # configure the rc file - configure_file( - ${CMAKE_SOURCE_DIR}/cmake/win/win.rc.in - ${TARGET_PATH}/win.rc - ) - # provide var for parent scope - set(${BINARY_NAME}_WIN_RC_PATH ${TARGET_PATH}/win.rc PARENT_SCOPE) -endfunction() diff --git a/debian/control.in b/debian/control.in deleted file mode 100644 index e52b6935..00000000 --- a/debian/control.in +++ /dev/null @@ -1,12 +0,0 @@ -Source: hyperion -Section: devel -Priority: optional -Build-Depends: @BUILD_DEPENDS@ -Standards-Version: @STANDARDS_VERSION@ -Maintainer: Hyperion Project -Homepage: https://hyperion-project.org/ - -Package: hyperion -Architecture: @ARCHITECTURE@ -Depends: @DEPENDS@ -Description: Hyperion is an opensource Bias or Ambient Lighting implementation which you might know from TV manufactures. It supports many LED devices and video grabbers. diff --git a/debian/distributions b/debian/distributions deleted file mode 100644 index 12da7848..00000000 --- a/debian/distributions +++ /dev/null @@ -1,58 +0,0 @@ -Origin: Hyperion-Project -Label: apt.hyperion-project.org -Codename: focal -Architectures: amd64 armhf arm64 -Components: main -Description: Official APT Repository by Hyperion Project -SignWith: yes - -Origin: Hyperion-Project -Label: apt.hyperion-project.org -Codename: jammy -Architectures: amd64 armhf arm64 -Components: main -Description: Official APT Repository by Hyperion Project -SignWith: yes - -Origin: Hyperion-Project -Label: apt.hyperion-project.org -Codename: kinetic -Architectures: amd64 armhf arm64 -Components: main -Description: Official APT Repository by Hyperion Project -SignWith: yes - -Origin: Hyperion-Project -Label: apt.hyperion-project.org -Codename: lunar -Architectures: amd64 -Components: main -Description: Official APT Repository by Hyperion Project -SignWith: yes - -Origin: Hyperion-Project -Label: apt.hyperion-project.org -Suite: oldstable -Codename: buster -Architectures: amd64 armhf arm64 -Components: main -Description: Official APT Repository by Hyperion Project -SignWith: yes - -Origin: Hyperion-Project -Label: apt.hyperion-project.org -Suite: stable -Codename: bullseye -Architectures: amd64 armhf arm64 -Components: main -Description: Official APT Repository by Hyperion Project -SignWith: yes - -Origin: Hyperion-Project -Label: apt.hyperion-project.org -Suite: unstable -Codename: bookworm -Architectures: amd64 -Components: main -Description: Official APT Repository by Hyperion Project -SignWith: yes diff --git a/debian/rules.in b/debian/rules.in deleted file mode 100644 index a8d28d84..00000000 --- a/debian/rules.in +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/make -f -export DH_VERBOSE = 1 - -BUILDDIR = build - -build: - mkdir $(BUILDDIR); - cd $(BUILDDIR); cmake @CMAKE_ENVIRONMENT@ -DCMAKE_INSTALL_PREFIX=../debian/tmp/usr .. - make -j4 -C $(BUILDDIR) - -binary: binary-indep binary-arch - -binary-indep: - -binary-arch: - cd $(BUILDDIR); cmake -P cmake_install.cmake - rm -rf debian/tmp/usr/include debian/tmp/usr/lib debian/tmp/usr/bin/flatc - mkdir debian/tmp/DEBIAN - cp cmake/package-scripts/postinst debian/tmp/DEBIAN - chmod 0775 debian/tmp/DEBIAN/postinst - cp cmake/package-scripts/preinst debian/tmp/DEBIAN - chmod 0775 debian/tmp/DEBIAN/preinst - cp cmake/package-scripts/prerm debian/tmp/DEBIAN - chmod 0775 debian/tmp/DEBIAN/prerm - dpkg-gencontrol -phyperion - dpkg --build debian/tmp .. - rm -rf debian/tmp $(BUILDDIR) - -clean: - rm -rf $(BUILDDIR) - -.PHONY: build binary binary-arch binary-indep clean diff --git a/dependencies/CMakeLists-qmdnsengine.txt.in b/dependencies/CMakeLists-qmdnsengine.txt.in deleted file mode 100644 index b3f0812a..00000000 --- a/dependencies/CMakeLists-qmdnsengine.txt.in +++ /dev/null @@ -1,26 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -project(qmdnsengine) - -set(WORK_DIR "@QMDNS_WORK_DIR@") -set(SOURCE_DIR "@QMDNS_SOURCE_DIR@") -set(INSTALL_DIR "@QMDNS_INSTALL_DIR@") -set(CMAKE_ARGS "@QMDNS_CMAKE_ARGS@") -set(QMDNS_LOGGING "@QMDNS_LOGGING@") - -include(ExternalProject) - -ExternalProject_Add(qmdnsengine - PREFIX ${WORK_DIR} - BUILD_ALWAYS OFF - DOWNLOAD_COMMAND "" - SOURCE_DIR ${SOURCE_DIR} - INSTALL_DIR ${INSTALL_DIR} - CMAKE_ARGS ${CMAKE_ARGS} - LOG_DOWNLOAD ${QMDNS_LOGGING} - LOG_UPDATE ${QMDNS_LOGGING} - LOG_CONFIGURE ${QMDNS_LOGGING} - LOG_BUILD ${QMDNS_LOGGING} - LOG_INSTALL ${QMDNS_LOGGING} - LOG_TEST ${QMDNS_LOGGING} -) diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index bc5044be..28ba5d04 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -24,46 +24,46 @@ if (ENABLE_MDNS) if (USE_SYSTEM_QMDNS_LIBS) find_package(qmdnsengine REQUIRED) else () - if (NOT DEFINED BUILD_QMDNS_ONCE) - set(BUILD_QMDNS_ONCE CACHE INTERNAL "Done") - set(QMDNS_WORK_DIR "${CMAKE_BINARY_DIR}/dependencies/external/qmdnsengine") - set(QMDNS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/qmdnsengine") - set(QMDNS_INSTALL_DIR ${CMAKE_BINARY_DIR}) - set(QMDNS_CMAKE_ARGS - -DBUILD_SHARED_LIBS:BOOL=OFF - -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR} - -DBIN_INSTALL_DIR:STRING=lib - -DLIB_INSTALL_DIR:STRING=lib - -DINCLUDE_INSTALL_DIR:STRING=include - -DCMAKE_PREFIX_PATH:PATH=${CMAKE_PREFIX_PATH} - -Wno-dev - ) - - if(${CMAKE_BUILD_TYPE} AND ${CMAKE_BUILD_TYPE} EQUAL "Debug") - set(QMDNS_LOGGING 1) - else () - set(QMDNS_LOGGING 0) - endif () - - configure_file(${CMAKE_SOURCE_DIR}/dependencies/CMakeLists-qmdnsengine.txt.in ${QMDNS_WORK_DIR}/CMakeLists.txt @ONLY) - execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${QMDNS_WORK_DIR}) - execute_process(COMMAND ${CMAKE_COMMAND} --build . --config "${CMAKE_BUILD_TYPE}" WORKING_DIRECTORY ${QMDNS_WORK_DIR}) - endif() - - set(QMDNS_INCLUDE_DIR "${CMAKE_BINARY_DIR}/include") - if(WIN32) set(QMDNS_LIBRARIES ${CMAKE_BINARY_DIR}/lib/qmdnsengine${CMAKE_STATIC_LIBRARY_SUFFIX}) else() set(QMDNS_LIBRARIES ${CMAKE_BINARY_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}qmdnsengine${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() - mark_as_advanced (QMDNS_INCLUDE_DIR QMDNS_LIBRARIES) - endif () + get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + set(QMDNS_CMAKE_ARGS + -DBUILD_SHARED_LIBS:BOOL=OFF + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR} + -DBIN_INSTALL_DIR:STRING=lib + -DLIB_INSTALL_DIR:STRING=lib + -DINCLUDE_INSTALL_DIR:STRING=include + -DCMAKE_PREFIX_PATH:PATH=${CMAKE_PREFIX_PATH} + $<$>:-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}> + -Wno-dev + ) - set(QMDNS_INCLUDE_DIR ${QMDNS_INCLUDE_DIR} PARENT_SCOPE) - set(QMDNS_LIBRARIES ${QMDNS_LIBRARIES} PARENT_SCOPE) - include_directories(${QMDNS_INCLUDE_DIR}) + include(ExternalProject) + ExternalProject_Add(qmdns + PREFIX ${CMAKE_BINARY_DIR}/dependencies/external/qmdnsengine + BUILD_ALWAYS OFF + DOWNLOAD_COMMAND "" + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/qmdnsengine + BINARY_DIR ${CMAKE_BINARY_DIR}/dependencies/external/qmdnsengine/build + CMAKE_ARGS ${QMDNS_CMAKE_ARGS} + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S -B ${QMDNS_CMAKE_ARGS} -G ${CMAKE_GENERATOR} + BUILD_COMMAND ${CMAKE_COMMAND} --build --config $ + INSTALL_DIR ${CMAKE_BINARY_DIR} + BUILD_BYPRODUCTS ${QMDNS_LIBRARIES} + ) + + add_library(qmdnsengine STATIC IMPORTED GLOBAL) + set_target_properties(qmdnsengine PROPERTIES + IMPORTED_LOCATION ${QMDNS_LIBRARIES} + INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_BINARY_DIR}/include + ) + + add_dependencies(qmdnsengine qmdns) + endif () endif() #============================================================================= @@ -75,8 +75,18 @@ if(ENABLE_FLATBUF_SERVER OR ENABLE_FLATBUF_CONNECT) if (USE_SYSTEM_FLATBUFFERS_LIBS) find_program(FLATBUFFERS_FLATC_EXECUTABLE NAMES flatc REQUIRED) - find_package(Flatbuffers REQUIRED) - else () + find_package(Flatbuffers QUIET) + if (NOT Flatbuffers_FOUND) + find_package(FlatBuffers QUIET) + if (NOT FlatBuffers_FOUND) + message(STATUS "Could not find Flatbuffers system library, build static Flatbuffers library") + set(DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS OFF PARENT_SCOPE) + set(USE_SYSTEM_FLATBUFFERS_LIBS OFF) + endif() + endif() + endif() + + if (NOT USE_SYSTEM_FLATBUFFERS_LIBS) set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared flatbuffers library") set(FLATBUFFERS_BUILD_TESTS OFF CACHE BOOL "Build Flatbuffers with tests") add_subdirectory(external/flatbuffers) @@ -133,9 +143,9 @@ endif() if(ENABLE_PROTOBUF_SERVER) set(USE_SYSTEM_PROTO_LIBS ${DEFAULT_USE_SYSTEM_PROTO_LIBS} CACHE BOOL "use protobuf library from system") - + # defines for 3rd party sub-modules - set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "Build abseil-cpp with C++ version requirements propagated") + set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "Build abseil-cpp with C++ version requirements propagated") if (USE_SYSTEM_PROTO_LIBS) find_package(Protobuf REQUIRED) @@ -163,7 +173,7 @@ if(ENABLE_PROTOBUF_SERVER) # define the protobuf library set(PROTOBUF_LIBRARIES protobuf::libprotobuf) - + endif() # redefine at parent scope diff --git a/dependencies/external/mbedtls b/dependencies/external/mbedtls index 1873d3bf..edb8fec9 160000 --- a/dependencies/external/mbedtls +++ b/dependencies/external/mbedtls @@ -1 +1 @@ -Subproject commit 1873d3bfc2da771672bd8e7e8f41f57e0af77f33 +Subproject commit edb8fec9882084344a314368ac7fd957a187519c diff --git a/include/grabber/OsxFrameGrabberMock.h b/include/grabber/OsxFrameGrabberMock.h deleted file mode 100644 index bdc622c4..00000000 --- a/include/grabber/OsxFrameGrabberMock.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once -#ifndef __APPLE__ - -/* - * this is a mock up for compiling and testing osx wrapper on no osx platform. - * this will show a test image and rotate the colors. - * - * see https://github.com/phracker/MacOSX-SDKs/blob/master/MacOSX10.8.sdk/System/Library/Frameworks/CoreGraphics.framework/Versions/A/Headers - * - */ - -#include -#include - -enum _CGError { - kCGErrorSuccess = 0, - kCGErrorFailure = 1000, - kCGErrorIllegalArgument = 1001, - kCGErrorInvalidConnection = 1002, - kCGErrorInvalidContext = 1003, - kCGErrorCannotComplete = 1004, - kCGErrorNotImplemented = 1006, - kCGErrorRangeCheck = 1007, - kCGErrorTypeCheck = 1008, - kCGErrorInvalidOperation = 1010, - kCGErrorNoneAvailable = 1011, - - /* Obsolete errors. */ - kCGErrorNameTooLong = 1005, - kCGErrorNoCurrentPoint = 1009, - kCGErrorApplicationRequiresNewerSystem = 1015, - kCGErrorApplicationNotPermittedToExecute = 1016, - kCGErrorApplicationIncorrectExecutableFormatFound = 1023, - kCGErrorApplicationIsLaunching = 1024, - kCGErrorApplicationAlreadyRunning = 1025, - kCGErrorApplicationCanOnlyBeRunInOneSessionAtATime = 1026, - kCGErrorClassicApplicationsMustBeLaunchedByClassic = 1027, - kCGErrorForkFailed = 1028, - kCGErrorRetryRegistration = 1029, - kCGErrorFirst = 1000, - kCGErrorLast = 1029 -}; -typedef int32_t CGError; -typedef double CGFloat; - -struct CGSize { - CGFloat width; - CGFloat height; -}; -typedef struct CGSize CGSize; - -struct CGPoint { - float x; - float y; -}; -typedef struct CGPoint CGPoint; - -struct CGRect { - CGPoint origin; - CGSize size; -}; -typedef struct CGRect CGRect; - -typedef CGError CGDisplayErr; -typedef uint32_t CGDirectDisplayID; -typedef uint32_t CGDisplayCount;; -typedef struct CGDisplayMode *CGDisplayModeRef; - -typedef Image CGImage; -typedef CGImage* CGImageRef; -typedef unsigned char CFData; -typedef CFData* CFDataRef; - -const int kCGDirectMainDisplay = 0; - -CGError CGGetActiveDisplayList(uint32_t maxDisplays, CGDirectDisplayID *activeDisplays, uint32_t *displayCount); -CGDisplayModeRef CGDisplayCopyDisplayMode(CGDirectDisplayID display); -CGRect CGDisplayBounds(CGDirectDisplayID display); -void CGDisplayModeRelease(CGDisplayModeRef mode); - -CGImageRef CGDisplayCreateImage(CGDirectDisplayID display); -void CGImageRelease(CGImageRef image); -CGImageRef CGImageGetDataProvider(CGImageRef image); -CFDataRef CGDataProviderCopyData(CGImageRef image); -unsigned char* CFDataGetBytePtr(CFDataRef imgData); -unsigned CGImageGetWidth(CGImageRef image); -unsigned CGImageGetHeight(CGImageRef image); -unsigned CGImageGetBitsPerPixel(CGImageRef image); -unsigned CGImageGetBytesPerRow(CGImageRef image); -void CFRelease(CFDataRef imgData); - - -#endif diff --git a/include/grabber/AmlogicGrabber.h b/include/grabber/amlogic/AmlogicGrabber.h similarity index 97% rename from include/grabber/AmlogicGrabber.h rename to include/grabber/amlogic/AmlogicGrabber.h index 97231354..f61742aa 100644 --- a/include/grabber/AmlogicGrabber.h +++ b/include/grabber/amlogic/AmlogicGrabber.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include /// /// diff --git a/include/grabber/AmlogicWrapper.h b/include/grabber/amlogic/AmlogicWrapper.h similarity index 95% rename from include/grabber/AmlogicWrapper.h rename to include/grabber/amlogic/AmlogicWrapper.h index 87796bcd..d97c45c1 100644 --- a/include/grabber/AmlogicWrapper.h +++ b/include/grabber/amlogic/AmlogicWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include /// /// The Amlogic uses an instance of the AmlogicGrabber to obtain ImageRgb's from the diff --git a/include/grabber/AudioGrabber.h b/include/grabber/audio/AudioGrabber.h similarity index 100% rename from include/grabber/AudioGrabber.h rename to include/grabber/audio/AudioGrabber.h diff --git a/include/grabber/AudioGrabberLinux.h b/include/grabber/audio/AudioGrabberLinux.h similarity index 86% rename from include/grabber/AudioGrabberLinux.h rename to include/grabber/audio/AudioGrabberLinux.h index 0f19ae6c..272ab2f5 100644 --- a/include/grabber/AudioGrabberLinux.h +++ b/include/grabber/audio/AudioGrabberLinux.h @@ -6,7 +6,7 @@ #include // Hyperion-utils includes -#include +#include /// /// @brief The Linux Audio capture implementation @@ -18,74 +18,69 @@ class AudioGrabberLinux : public AudioGrabber AudioGrabberLinux(); ~AudioGrabberLinux() override; - /// + /// /// Process audio buffer /// void processAudioBuffer(snd_pcm_sframes_t frames); - /// + /// /// Is Running Flag /// std::atomic _isRunning; - /// + /// /// Current capture device /// snd_pcm_t * _captureDevice; public slots: - /// + /// /// Start audio capturing session /// /// @returns true if successful bool start() override; - /// + /// /// Stop audio capturing session /// void stop() override; - /// + /// /// Discovery audio devices /// QJsonArray discover(const QJsonObject& params) override; private: - /// + /// /// Refresh audio devices /// void refreshDevices(); - /// + /// /// Configure current audio capture interface /// bool configureCaptureInterface(); - /// + /// /// Get device name from path /// QString getDeviceName(const QString& devicePath) const; - /// + /// /// Current sample rate /// unsigned int _sampleRate; - /// + /// /// Audio capture thread /// pthread_t _audioThread; - /// + /// /// ALSA device configuration parameters /// snd_pcm_hw_params_t * _captureDeviceConfig; }; -/// -/// Audio processing thread function -/// -static void* AudioThreadRunner(void* params); - #endif // AUDIOGRABBERLINUX_H diff --git a/include/grabber/AudioGrabberWindows.h b/include/grabber/audio/AudioGrabberWindows.h similarity index 98% rename from include/grabber/AudioGrabberWindows.h rename to include/grabber/audio/AudioGrabberWindows.h index 9c3945b6..ee7f53c4 100644 --- a/include/grabber/AudioGrabberWindows.h +++ b/include/grabber/audio/AudioGrabberWindows.h @@ -2,7 +2,7 @@ #define AUDIOGRABBERWINDOWS_H // Hyperion-utils includes -#include +#include #include /// @@ -14,7 +14,7 @@ class AudioGrabberWindows : public AudioGrabber AudioGrabberWindows(); ~AudioGrabberWindows() override; - + public slots: bool start() override; void stop() override; diff --git a/include/grabber/AudioWrapper.h b/include/grabber/audio/AudioWrapper.h similarity index 90% rename from include/grabber/AudioWrapper.h rename to include/grabber/audio/AudioWrapper.h index 9e13c933..4f301790 100644 --- a/include/grabber/AudioWrapper.h +++ b/include/grabber/audio/AudioWrapper.h @@ -3,14 +3,14 @@ #include #ifdef WIN32 - #include + #include #endif #ifdef __linux__ - #include + #include #endif -/// +/// /// Audio Grabber wrapper /// class AudioWrapper : public GrabberWrapper @@ -32,7 +32,7 @@ class AudioWrapper : public GrabberWrapper /// ~AudioWrapper() override; - /// + /// /// Settings update handler /// void handleSettingsUpdate(settings::type type, const QJsonDocument& config) override; @@ -43,13 +43,13 @@ class AudioWrapper : public GrabberWrapper /// void action() override; - /// + /// /// Start audio capturing session /// /// @returns true if successful bool start() override; - /// + /// /// Stop audio capturing session /// void stop() override; diff --git a/include/grabber/DirectXGrabber.h b/include/grabber/directx/DirectXGrabber.h similarity index 100% rename from include/grabber/DirectXGrabber.h rename to include/grabber/directx/DirectXGrabber.h diff --git a/include/grabber/DirectXWrapper.h b/include/grabber/directx/DirectXWrapper.h similarity index 96% rename from include/grabber/DirectXWrapper.h rename to include/grabber/directx/DirectXWrapper.h index d063497d..56ec6879 100644 --- a/include/grabber/DirectXWrapper.h +++ b/include/grabber/directx/DirectXWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include class DirectXWrapper: public GrabberWrapper { diff --git a/include/grabber/DispmanxFrameGrabber.h b/include/grabber/dispmanx/DispmanxFrameGrabber.h similarity index 100% rename from include/grabber/DispmanxFrameGrabber.h rename to include/grabber/dispmanx/DispmanxFrameGrabber.h diff --git a/include/grabber/DispmanxWrapper.h b/include/grabber/dispmanx/DispmanxWrapper.h similarity index 95% rename from include/grabber/DispmanxWrapper.h rename to include/grabber/dispmanx/DispmanxWrapper.h index 303391fa..499316fc 100644 --- a/include/grabber/DispmanxWrapper.h +++ b/include/grabber/dispmanx/DispmanxWrapper.h @@ -3,7 +3,7 @@ // Utils includes #include #include -#include +#include /// /// The DispmanxWrapper uses an instance of the DispmanxFrameGrabber to obtain ImageRgb's from the diff --git a/include/grabber/FramebufferFrameGrabber.h b/include/grabber/framebuffer/FramebufferFrameGrabber.h similarity index 100% rename from include/grabber/FramebufferFrameGrabber.h rename to include/grabber/framebuffer/FramebufferFrameGrabber.h diff --git a/include/grabber/FramebufferWrapper.h b/include/grabber/framebuffer/FramebufferWrapper.h similarity index 94% rename from include/grabber/FramebufferWrapper.h rename to include/grabber/framebuffer/FramebufferWrapper.h index 2098d362..084eb96c 100644 --- a/include/grabber/FramebufferWrapper.h +++ b/include/grabber/framebuffer/FramebufferWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include /// /// The FramebufferWrapper uses an instance of the FramebufferFrameGrabber to obtain ImageRgb's from the diff --git a/include/grabber/OsxFrameGrabber.h b/include/grabber/osx/OsxFrameGrabber.h similarity index 95% rename from include/grabber/OsxFrameGrabber.h rename to include/grabber/osx/OsxFrameGrabber.h index 17530888..afb430fc 100644 --- a/include/grabber/OsxFrameGrabber.h +++ b/include/grabber/osx/OsxFrameGrabber.h @@ -1,11 +1,7 @@ #pragma once // OSX includes -#ifdef __APPLE__ #include -#else -#include -#endif // Utils includes #include diff --git a/include/grabber/OsxWrapper.h b/include/grabber/osx/OsxWrapper.h similarity index 95% rename from include/grabber/OsxWrapper.h rename to include/grabber/osx/OsxWrapper.h index c9520f7e..fd367184 100644 --- a/include/grabber/OsxWrapper.h +++ b/include/grabber/osx/OsxWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include /// /// The OsxWrapper uses an instance of the OsxFrameGrabber to obtain ImageRgb's from the displayed content. diff --git a/include/grabber/QtGrabber.h b/include/grabber/qt/QtGrabber.h similarity index 100% rename from include/grabber/QtGrabber.h rename to include/grabber/qt/QtGrabber.h diff --git a/include/grabber/QtWrapper.h b/include/grabber/qt/QtWrapper.h similarity index 97% rename from include/grabber/QtWrapper.h rename to include/grabber/qt/QtWrapper.h index 3bba4a82..3df2a64e 100644 --- a/include/grabber/QtWrapper.h +++ b/include/grabber/qt/QtWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include /// /// The QtWrapper uses QtFramework API's to get a picture from system diff --git a/include/grabber/EncoderThread.h b/include/grabber/video/EncoderThread.h similarity index 91% rename from include/grabber/EncoderThread.h rename to include/grabber/video/EncoderThread.h index 93a3ed88..51008d3e 100644 --- a/include/grabber/EncoderThread.h +++ b/include/grabber/video/EncoderThread.h @@ -136,11 +136,11 @@ class EncoderThreadManager : public QObject public: explicit EncoderThreadManager(QObject *parent = nullptr) : QObject(parent) - , _threadCount(static_cast(qMax(QThread::idealThreadCount(), DEFAULT_THREAD_COUNT))) + , _threadCount(qMax(QThread::idealThreadCount(), DEFAULT_THREAD_COUNT)) , _threads(nullptr) { _threads = new Thread*[_threadCount]; - for (unsigned long i = 0; i < _threadCount; i++) + for (int i = 0; i < _threadCount; i++) { _threads[i] = new Thread(new EncoderThread, this); _threads[i]->setObjectName("Encoder " + QString::number(i)); @@ -151,7 +151,7 @@ public: { if (_threads != nullptr) { - for(unsigned long i = 0; i < _threadCount; i++) + for(int i = 0; i < _threadCount; i++) { _threads[i]->deleteLater(); _threads[i] = nullptr; @@ -165,18 +165,18 @@ public: void start() { if (_threads != nullptr) - for (unsigned long i = 0; i < _threadCount; i++) + for (int i = 0; i < _threadCount; i++) connect(_threads[i]->thread(), &EncoderThread::newFrame, this, &EncoderThreadManager::newFrame); } void stop() { if (_threads != nullptr) - for(unsigned long i = 0; i < _threadCount; i++) + for(int i = 0; i < _threadCount; i++) disconnect(_threads[i]->thread(), nullptr, nullptr, nullptr); } - unsigned long _threadCount; + int _threadCount; Thread** _threads; signals: diff --git a/include/grabber/VideoWrapper.h b/include/grabber/video/VideoWrapper.h similarity index 89% rename from include/grabber/VideoWrapper.h rename to include/grabber/video/VideoWrapper.h index 932ff1ab..19422f25 100644 --- a/include/grabber/VideoWrapper.h +++ b/include/grabber/video/VideoWrapper.h @@ -4,9 +4,9 @@ #include #if defined(ENABLE_MF) - #include + #include #elif defined(ENABLE_V4L2) - #include + #include #endif #if defined(ENABLE_CEC) diff --git a/include/grabber/MFGrabber.h b/include/grabber/video/mediafoundation/MFGrabber.h similarity index 98% rename from include/grabber/MFGrabber.h rename to include/grabber/video/mediafoundation/MFGrabber.h index f778f8e8..30142069 100644 --- a/include/grabber/MFGrabber.h +++ b/include/grabber/video/mediafoundation/MFGrabber.h @@ -21,7 +21,7 @@ #include // decoder thread includes -#include +#include /// Forward class declaration class SourceReaderCB; diff --git a/include/grabber/V4L2Grabber.h b/include/grabber/video/v4l2/V4L2Grabber.h similarity index 99% rename from include/grabber/V4L2Grabber.h rename to include/grabber/video/v4l2/V4L2Grabber.h index 22f8cdba..6c72be94 100644 --- a/include/grabber/V4L2Grabber.h +++ b/include/grabber/video/v4l2/V4L2Grabber.h @@ -19,7 +19,7 @@ #include // decoder thread includes -#include +#include // Determine the cmake options #include diff --git a/include/grabber/X11Grabber.h b/include/grabber/x11/X11Grabber.h similarity index 100% rename from include/grabber/X11Grabber.h rename to include/grabber/x11/X11Grabber.h diff --git a/include/grabber/X11Wrapper.h b/include/grabber/x11/X11Wrapper.h similarity index 97% rename from include/grabber/X11Wrapper.h rename to include/grabber/x11/X11Wrapper.h index 79b6da92..dae62ad7 100644 --- a/include/grabber/X11Wrapper.h +++ b/include/grabber/x11/X11Wrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include // some include of xorg defines "None" this is also used by QT and has to be undefined to avoid collisions #ifdef None #undef None diff --git a/include/grabber/XcbGrabber.h b/include/grabber/xcb/XcbGrabber.h similarity index 100% rename from include/grabber/XcbGrabber.h rename to include/grabber/xcb/XcbGrabber.h diff --git a/include/grabber/XcbWrapper.h b/include/grabber/xcb/XcbWrapper.h similarity index 94% rename from include/grabber/XcbWrapper.h rename to include/grabber/xcb/XcbWrapper.h index 71bb70ea..0292937c 100644 --- a/include/grabber/XcbWrapper.h +++ b/include/grabber/xcb/XcbWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include // some include of xorg defines "None" this is also used by QT and has to be undefined to avoid collisions #ifdef None diff --git a/include/hyperion/ImageToLedsMap.h b/include/hyperion/ImageToLedsMap.h index 45e7bb5a..d720581a 100644 --- a/include/hyperion/ImageToLedsMap.h +++ b/include/hyperion/ImageToLedsMap.h @@ -555,7 +555,7 @@ namespace hyperion if (pixelNum > 0) { // initial cluster with different colors - auto clusters = std::unique_ptr< ColorCluster >(new ColorCluster[_clusterCount]); + std::unique_ptr[]> clusters(new ColorCluster[_clusterCount]); for(int k = 0; k < _clusterCount; ++k) { clusters.get()[k].newColor = DEFAULT_CLUSTER_COLORS[k]; diff --git a/include/utils/Process.h b/include/utils/Process.h index f8da8e65..07df2fe7 100644 --- a/include/utils/Process.h +++ b/include/utils/Process.h @@ -3,9 +3,8 @@ #include #include -namespace Process { - -void restartHyperion(int exitCode = 0); -QByteArray command_exec(const QString& cmd, const QByteArray& data = {}); - +namespace Process +{ + void restartHyperion(int exitCode = 0); + QByteArray command_exec(const QString& cmd, const QByteArray& data = {}); } diff --git a/libsrc/CMakeLists.txt b/libsrc/CMakeLists.txt index ba5716ba..7acad42a 100644 --- a/libsrc/CMakeLists.txt +++ b/libsrc/CMakeLists.txt @@ -1,7 +1,7 @@ # Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc) +set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include) +set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc) add_subdirectory(hyperion) add_subdirectory(commandline) @@ -34,7 +34,7 @@ add_subdirectory(ssdp) if(ENABLE_MDNS) add_subdirectory(mdns) -endif() +endif() if(ENABLE_EFFECTENGINE) add_subdirectory(effectengine) diff --git a/libsrc/api/CMakeLists.txt b/libsrc/api/CMakeLists.txt index 3fd822ff..ac5cdcfd 100644 --- a/libsrc/api/CMakeLists.txt +++ b/libsrc/api/CMakeLists.txt @@ -1,26 +1,20 @@ -# Define the current source locations - -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/api) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/api) - -FILE ( GLOB_RECURSE Api_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -set(Api_RESOURCES ${CURRENT_SOURCE_DIR}/JSONRPC_schemas.qrc ) - add_library(hyperion-api - ${Api_SOURCES} - ${Api_RESOURCES} + ${CMAKE_SOURCE_DIR}/include/api/apiStructs.h + ${CMAKE_SOURCE_DIR}/include/api/API.h + ${CMAKE_SOURCE_DIR}/include/api/JsonAPI.h + ${CMAKE_SOURCE_DIR}/include/api/JsonCB.h + ${CMAKE_SOURCE_DIR}/libsrc/api/JsonAPI.cpp + ${CMAKE_SOURCE_DIR}/libsrc/api/API.cpp + ${CMAKE_SOURCE_DIR}/libsrc/api/JsonCB.cpp + ${CMAKE_SOURCE_DIR}/libsrc/api/JSONRPC_schemas.qrc ) -if(ENABLE_DX) - include_directories(${DIRECTX9_INCLUDE_DIRS}) - target_link_libraries(hyperion-api ${DIRECTX9_LIBRARIES}) -endif(ENABLE_DX) - target_link_libraries(hyperion-api hyperion hyperion-utils - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Gui - Qt${QT_VERSION_MAJOR}::Network + ${DIRECTX9_LIBRARIES} +) + +target_include_directories(hyperion-api PRIVATE + ${DIRECTX9_INCLUDE_DIRS} ) diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 75863ca2..abc62a6f 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -19,54 +19,54 @@ #include // Required to determine the cmake options #include -#include +#include #include #if defined(ENABLE_MF) - #include + #include #elif defined(ENABLE_V4L2) - #include + #include #endif #if defined(ENABLE_AUDIO) - #include + #include #ifdef WIN32 - #include + #include #endif #ifdef __linux__ - #include + #include #endif #endif #if defined(ENABLE_X11) - #include + #include #endif #if defined(ENABLE_XCB) - #include + #include #endif #if defined(ENABLE_DX) - #include + #include #endif #if defined(ENABLE_FB) - #include + #include #endif #if defined(ENABLE_DISPMANX) - #include + #include #endif #if defined(ENABLE_AMLOGIC) - #include + #include #endif #if defined(ENABLE_OSX) - #include + #include #endif #include diff --git a/libsrc/blackborder/CMakeLists.txt b/libsrc/blackborder/CMakeLists.txt index e93c5e47..d49454d8 100644 --- a/libsrc/blackborder/CMakeLists.txt +++ b/libsrc/blackborder/CMakeLists.txt @@ -1,11 +1,9 @@ - -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/blackborder) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/blackborder) - -FILE ( GLOB Blackborder_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(blackborder ${Blackborder_SOURCES} ) +add_library(blackborder + ${CMAKE_SOURCE_DIR}/include/blackborder/BlackBorderDetector.h + ${CMAKE_SOURCE_DIR}/include/blackborder/BlackBorderProcessor.h + ${CMAKE_SOURCE_DIR}/libsrc/blackborder/BlackBorderDetector.cpp + ${CMAKE_SOURCE_DIR}/libsrc/blackborder/BlackBorderProcessor.cpp +) target_link_libraries(blackborder hyperion-utils diff --git a/libsrc/boblightserver/CMakeLists.txt b/libsrc/boblightserver/CMakeLists.txt index 1587d4df..44ff5162 100644 --- a/libsrc/boblightserver/CMakeLists.txt +++ b/libsrc/boblightserver/CMakeLists.txt @@ -1,14 +1,11 @@ - -# Define the current source locations -set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/boblightserver) -set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/boblightserver) - -FILE ( GLOB BoblightServer_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(boblightserver ${BoblightServer_SOURCES} ) +add_library(boblightserver + ${CMAKE_SOURCE_DIR}/include/boblightserver/BoblightServer.h + ${CMAKE_SOURCE_DIR}/libsrc/boblightserver/BoblightServer.cpp + ${CMAKE_SOURCE_DIR}/libsrc/boblightserver/BoblightClientConnection.h + ${CMAKE_SOURCE_DIR}/libsrc/boblightserver/BoblightClientConnection.cpp +) target_link_libraries(boblightserver hyperion hyperion-utils - ${QT_LIBRARIES} ) diff --git a/libsrc/cec/CMakeLists.txt b/libsrc/cec/CMakeLists.txt index dfd80e7d..711c33d1 100644 --- a/libsrc/cec/CMakeLists.txt +++ b/libsrc/cec/CMakeLists.txt @@ -1,19 +1,18 @@ -# Define the current source locations find_package(CEC REQUIRED) +if(CEC_FOUND) + list(GET CEC_LIBRARIES 0 CEC_LIBRARIES) + add_definitions(-DCEC_LIBRARY="${CEC_LIBRARIES}") +else() + message(FATAL_ERROR "libCEC not found") +endif() -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/cec) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/cec) - -FILE (GLOB CEC_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp") - -add_library(cechandler ${CEC_SOURCES}) -list(GET CEC_LIBRARIES 0 CEC_LIBRARIES) -add_definitions(-DCEC_LIBRARY="${CEC_LIBRARIES}") - -include_directories(${CEC_INCLUDE_DIRS}) +add_library(cechandler + ${CMAKE_SOURCE_DIR}/include/cec/CECEvent.h + ${CMAKE_SOURCE_DIR}/include/cec/CECHandler.h + ${CMAKE_SOURCE_DIR}/libsrc/cec/CECHandler.cpp +) target_link_libraries(cechandler Qt${QT_VERSION_MAJOR}::Core ${CMAKE_DL_LIBS} ) - diff --git a/libsrc/commandline/CMakeLists.txt b/libsrc/commandline/CMakeLists.txt index 63a403ac..cd764913 100644 --- a/libsrc/commandline/CMakeLists.txt +++ b/libsrc/commandline/CMakeLists.txt @@ -1,10 +1,27 @@ -# Define the current source locations -set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/commandline) -set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/commandline) - -FILE ( GLOB Parser_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(commandline ${Parser_SOURCES} ) +add_library(commandline + ${CMAKE_SOURCE_DIR}/include/commandline/BooleanOption.h + ${CMAKE_SOURCE_DIR}/include/commandline/ColorOption.h + ${CMAKE_SOURCE_DIR}/include/commandline/ColorsOption.h + ${CMAKE_SOURCE_DIR}/include/commandline/DoubleOption.h + ${CMAKE_SOURCE_DIR}/include/commandline/ImageOption.h + ${CMAKE_SOURCE_DIR}/include/commandline/IntOption.h + ${CMAKE_SOURCE_DIR}/include/commandline/Option.h + ${CMAKE_SOURCE_DIR}/include/commandline/Parser.h + ${CMAKE_SOURCE_DIR}/include/commandline/RegularExpressionOption.h + ${CMAKE_SOURCE_DIR}/include/commandline/SwitchOption.h + ${CMAKE_SOURCE_DIR}/include/commandline/ValidatorOption.h + ${CMAKE_SOURCE_DIR}/libsrc/commandline/BooleanOption.cpp + ${CMAKE_SOURCE_DIR}/libsrc/commandline/ColorOption.cpp + ${CMAKE_SOURCE_DIR}/libsrc/commandline/ColorsOption.cpp + ${CMAKE_SOURCE_DIR}/libsrc/commandline/DoubleOption.cpp + ${CMAKE_SOURCE_DIR}/libsrc/commandline/ImageOption.cpp + ${CMAKE_SOURCE_DIR}/libsrc/commandline/IntOption.cpp + ${CMAKE_SOURCE_DIR}/libsrc/commandline/Option.cpp + ${CMAKE_SOURCE_DIR}/libsrc/commandline/Parser.cpp + ${CMAKE_SOURCE_DIR}/libsrc/commandline/RegularExpressionOption.cpp + ${CMAKE_SOURCE_DIR}/libsrc/commandline/SwitchOption.cpp + ${CMAKE_SOURCE_DIR}/libsrc/commandline/ValidatorOption.cpp +) target_link_libraries(commandline hyperion diff --git a/libsrc/db/CMakeLists.txt b/libsrc/db/CMakeLists.txt index b81991e6..1beb3fe5 100644 --- a/libsrc/db/CMakeLists.txt +++ b/libsrc/db/CMakeLists.txt @@ -1,16 +1,14 @@ -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/db) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/db) - -FILE ( GLOB DB_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - add_library(database - ${DB_SOURCES} + ${CMAKE_SOURCE_DIR}/include/db/AuthTable.h + ${CMAKE_SOURCE_DIR}/include/db/DBManager.h + ${CMAKE_SOURCE_DIR}/include/db/InstanceTable.h + ${CMAKE_SOURCE_DIR}/include/db/MetaTable.h + ${CMAKE_SOURCE_DIR}/include/db/SettingsTable.h + ${CMAKE_SOURCE_DIR}/libsrc/db/DBManager.cpp ) target_link_libraries(database hyperion hyperion-utils - Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Sql ) diff --git a/libsrc/effectengine/CMakeLists.txt b/libsrc/effectengine/CMakeLists.txt index 0059648e..27ba48c0 100644 --- a/libsrc/effectengine/CMakeLists.txt +++ b/libsrc/effectengine/CMakeLists.txt @@ -1,47 +1,30 @@ -if (NOT CMAKE_VERSION VERSION_LESS "3.12") - find_package(Python3 COMPONENTS Interpreter Development REQUIRED) -else() - find_package (PythonLibs ${PYTHON_VERSION_STRING} EXACT) # Maps PythonLibs to the PythonInterp version of the main cmake -endif() +file(GLOB effectFiles RELATIVE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/effects/*) +set(HYPERION_EFFECTS_RES "") +foreach(f ${effectFiles}) + get_filename_component(fname ${f} NAME) + set(HYPERION_EFFECTS_RES "${HYPERION_EFFECTS_RES}\n\t\t${f}") +endforeach() -# Include the python directory. Also include the parent (which is for example /usr/include) -# which may be required when it is not includes by the (cross-) compiler by default. -if (NOT CMAKE_VERSION VERSION_LESS "3.12") - include_directories(${Python3_INCLUDE_DIRS} ${Python3_INCLUDE_DIRS}/..) -else() - include_directories(${PYTHON_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}/..) -endif() - -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/effectengine) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/effectengine) - -FILE ( GLOB EffectEngineSOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) -FILE ( GLOB effectFiles RELATIVE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/effects/* ) - -SET ( HYPERION_EFFECTS_RES "") -FOREACH( f ${effectFiles} ) - GET_FILENAME_COMPONENT(fname ${f} NAME) - SET(HYPERION_EFFECTS_RES "${HYPERION_EFFECTS_RES}\n\t\t${f}") -ENDFOREACH() -CONFIGURE_FILE(${CURRENT_SOURCE_DIR}/EffectEngine.qrc.in ${CMAKE_BINARY_DIR}/EffectEngine.qrc ) - -SET(EffectEngine_RESOURCES ${CMAKE_BINARY_DIR}/EffectEngine.qrc) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/EffectEngine.qrc.in ${CMAKE_BINARY_DIR}/EffectEngine.qrc) add_library(effectengine - ${EffectEngine_RESOURCES} - ${EffectEngineSOURCES} + ${CMAKE_BINARY_DIR}/EffectEngine.qrc + ${CMAKE_SOURCE_DIR}/include/effectengine/ActiveEffectDefinition.h + ${CMAKE_SOURCE_DIR}/include/effectengine/Effect.h + ${CMAKE_SOURCE_DIR}/include/effectengine/EffectDefinition.h + ${CMAKE_SOURCE_DIR}/include/effectengine/EffectEngine.h + ${CMAKE_SOURCE_DIR}/include/effectengine/EffectFileHandler.h + ${CMAKE_SOURCE_DIR}/include/effectengine/EffectModule.h + ${CMAKE_SOURCE_DIR}/include/effectengine/EffectSchema.h + ${CMAKE_SOURCE_DIR}/libsrc/effectengine/Effect.cpp + ${CMAKE_SOURCE_DIR}/libsrc/effectengine/EffectEngine.cpp + ${CMAKE_SOURCE_DIR}/libsrc/effectengine/EffectFileHandler.cpp + ${CMAKE_SOURCE_DIR}/libsrc/effectengine/EffectModule.cpp ) target_link_libraries(effectengine - hyperion python + hyperion Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui ) - -if (NOT CMAKE_VERSION VERSION_LESS "3.12") - target_link_libraries( effectengine ${Python3_LIBRARIES} ) -else() - target_link_libraries( effectengine ${PYTHON_LIBRARIES} ) -endif() diff --git a/libsrc/effectengine/EffectEngine.qrc.in b/libsrc/effectengine/EffectEngine.qrc.in index 2816f96b..2e43ab6d 100644 --- a/libsrc/effectengine/EffectEngine.qrc.in +++ b/libsrc/effectengine/EffectEngine.qrc.in @@ -1,6 +1,6 @@ - ${CURRENT_SOURCE_DIR}/EffectDefinition.schema.json + ${CMAKE_CURRENT_SOURCE_DIR}/EffectDefinition.schema.json ${HYPERION_EFFECTS_RES} diff --git a/libsrc/flatbufserver/CMakeLists.txt b/libsrc/flatbufserver/CMakeLists.txt index 836652f8..5e879517 100644 --- a/libsrc/flatbufserver/CMakeLists.txt +++ b/libsrc/flatbufserver/CMakeLists.txt @@ -1,69 +1,54 @@ +# set (compiled) Flatbuffer schema names +set(FBS_Request "hyperion_request") +set(FBS_Reply "hyperion_reply") -# Define the current source locations -set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/flatbufserver) -set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/flatbufserver) - -include_directories( - ${CMAKE_CURRENT_BINARY_DIR} - ${FLATBUFFERS_INCLUDE_DIRS} -) - -set(Flatbuffer_GENERATED_FBS - hyperion_reply_generated.h - hyperion_request_generated.h -) - -set(Flatbuffer_FBS - ${CURRENT_SOURCE_DIR}/hyperion_reply.fbs - ${CURRENT_SOURCE_DIR}/hyperion_request.fbs -) -FOREACH(FBS_FILE ${Flatbuffer_FBS}) - compile_flattbuffer_schema(${FBS_FILE} ${CMAKE_CURRENT_SOURCE_DIR}) -ENDFOREACH(FBS_FILE) +# define and compile flatbuffer schemas +list(APPEND Compiled_FBS ${FBS_Request}_generated.h) +list(APPEND Compiled_FBS ${FBS_Reply}_generated.h) +compile_flattbuffer_schema(${CMAKE_CURRENT_SOURCE_DIR}/${FBS_Request}.fbs ${CMAKE_CURRENT_SOURCE_DIR}) +compile_flattbuffer_schema(${CMAKE_CURRENT_SOURCE_DIR}/${FBS_Reply}.fbs ${CMAKE_CURRENT_SOURCE_DIR}) # let cmake know about new generated source files -set_source_files_properties( - ${Flatbuffer_GENERATED_FBS} PROPERTIES GENERATED TRUE -) - -### Split flatbufconnect from flatbufserver as flatbufserver relates to HyperionDaemon +set_source_files_properties(${Compiled_FBS} PROPERTIES GENERATED TRUE) +# split flatbufconnect from flatbufserver as flatbufserver relates to HyperionDaemon if(ENABLE_FLATBUF_CONNECT) -add_library(flatbufconnect - ${CURRENT_HEADER_DIR}/FlatBufferConnection.h - ${CURRENT_SOURCE_DIR}/FlatBufferConnection.cpp - ${FLATBUFSERVER_SOURCES} - ${Flatbuffer_GENERATED_FBS} + add_library(flatbufconnect + ${CMAKE_SOURCE_DIR}/include/flatbufserver/FlatBufferConnection.h + ${CMAKE_SOURCE_DIR}/libsrc/flatbufserver/FlatBufferConnection.cpp + ${Compiled_FBS} + ) -) -target_link_libraries(flatbufconnect - hyperion-utils - flatbuffers - Qt${QT_VERSION_MAJOR}::Network - Qt${QT_VERSION_MAJOR}::Core -) + target_link_libraries(flatbufconnect + hyperion-utils + flatbuffers + ) + + target_include_directories(flatbufconnect PUBLIC + ${FLATBUFFERS_INCLUDE_DIRS} + ) endif() if(ENABLE_FLATBUF_SERVER) -add_library(flatbufserver - ${CURRENT_HEADER_DIR}/FlatBufferServer.h - ${CURRENT_SOURCE_DIR}/FlatBufferServer.cpp - ${CURRENT_SOURCE_DIR}/FlatBufferClient.h - ${CURRENT_SOURCE_DIR}/FlatBufferClient.cpp - ${FLATBUFSERVER_SOURCES} - ${Flatbuffer_GENERATED_FBS} -) + add_library(flatbufserver + ${CMAKE_SOURCE_DIR}/include/flatbufserver/FlatBufferServer.h + ${CMAKE_SOURCE_DIR}/libsrc/flatbufserver/FlatBufferServer.cpp + ${CMAKE_SOURCE_DIR}/libsrc/flatbufserver/FlatBufferClient.h + ${CMAKE_SOURCE_DIR}/libsrc/flatbufserver/FlatBufferClient.cpp + ${Compiled_FBS} + ) -target_link_libraries(flatbufserver -hyperion-utils -flatbuffers -Qt${QT_VERSION_MAJOR}::Network -Qt${QT_VERSION_MAJOR}::Core -) - -if(ENABLE_MDNS) - target_link_libraries(flatbufserver mdns) -endif() + target_link_libraries(flatbufserver + hyperion-utils + flatbuffers + ) + target_include_directories(flatbufserver PUBLIC + ${FLATBUFFERS_INCLUDE_DIRS} + ) + + if(ENABLE_MDNS) + target_link_libraries(flatbufserver mdns) + endif() endif() diff --git a/libsrc/forwarder/CMakeLists.txt b/libsrc/forwarder/CMakeLists.txt index 60dc5a6b..a9ee88c1 100644 --- a/libsrc/forwarder/CMakeLists.txt +++ b/libsrc/forwarder/CMakeLists.txt @@ -1,22 +1,11 @@ - -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/forwarder) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/forwarder) - -if(ENABLE_FLATBUF_CONNECT) -include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver +add_library(forwarder + ${CMAKE_SOURCE_DIR}/include/forwarder/MessageForwarder.h + ${CMAKE_SOURCE_DIR}/libsrc/forwarder/MessageForwarder.cpp ) -endif() - -FILE ( GLOB Forwarder_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(forwarder ${Forwarder_SOURCES} ) target_link_libraries(forwarder hyperion hyperion-utils - ${QT_LIBRARIES} ) if(ENABLE_FLATBUF_CONNECT) diff --git a/libsrc/grabber/CMakeLists.txt b/libsrc/grabber/CMakeLists.txt index c729ad0a..75f8c529 100644 --- a/libsrc/grabber/CMakeLists.txt +++ b/libsrc/grabber/CMakeLists.txt @@ -1,39 +1,39 @@ -if (ENABLE_AMLOGIC) +if(ENABLE_AMLOGIC) add_subdirectory(amlogic) endif (ENABLE_AMLOGIC) -if (ENABLE_DISPMANX) +if(ENABLE_DISPMANX) add_subdirectory(dispmanx) endif (ENABLE_DISPMANX) -if (ENABLE_FB) +if(ENABLE_FB) add_subdirectory(framebuffer) endif (ENABLE_FB) -if (ENABLE_OSX) +if(ENABLE_OSX) add_subdirectory(osx) endif(ENABLE_OSX) -if (ENABLE_V4L2 OR ENABLE_MF) +if(ENABLE_V4L2 OR ENABLE_MF) add_subdirectory(video) -endif () +endif() -if (ENABLE_X11) +if(ENABLE_X11) add_subdirectory(x11) endif(ENABLE_X11) -if (ENABLE_XCB) +if(ENABLE_XCB) add_subdirectory(xcb) endif(ENABLE_XCB) -if (ENABLE_QT) +if(ENABLE_QT) add_subdirectory(qt) endif(ENABLE_QT) -if (ENABLE_DX) +if(ENABLE_DX) add_subdirectory(directx) endif(ENABLE_DX) -if (ENABLE_AUDIO) +if(ENABLE_AUDIO) add_subdirectory(audio) endif() diff --git a/libsrc/grabber/amlogic/AmlogicGrabber.cpp b/libsrc/grabber/amlogic/AmlogicGrabber.cpp index 4c590d0c..0f6b0587 100644 --- a/libsrc/grabber/amlogic/AmlogicGrabber.cpp +++ b/libsrc/grabber/amlogic/AmlogicGrabber.cpp @@ -20,7 +20,7 @@ // Local includes #include -#include +#include #include "Amvideocap.h" // Constants diff --git a/libsrc/grabber/amlogic/AmlogicWrapper.cpp b/libsrc/grabber/amlogic/AmlogicWrapper.cpp index 25581b2c..ac371ba0 100644 --- a/libsrc/grabber/amlogic/AmlogicWrapper.cpp +++ b/libsrc/grabber/amlogic/AmlogicWrapper.cpp @@ -1,4 +1,4 @@ -#include +#include AmlogicWrapper::AmlogicWrapper(int pixelDecimation, int updateRate_Hz) : GrabberWrapper("Amlogic", &_grabber, updateRate_Hz) diff --git a/libsrc/grabber/amlogic/CMakeLists.txt b/libsrc/grabber/amlogic/CMakeLists.txt index 284f8b5f..d59066d0 100644 --- a/libsrc/grabber/amlogic/CMakeLists.txt +++ b/libsrc/grabber/amlogic/CMakeLists.txt @@ -1,13 +1,15 @@ -INCLUDE (CheckIncludeFiles) - -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic) - -FILE ( GLOB AmlogicSOURCES "${CURRENT_HEADER_DIR}/Amlogic*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(amlogic-grabber ${AmlogicSOURCES} ) +add_library(amlogic-grabber + ${CMAKE_SOURCE_DIR}/include/grabber/amlogic/AmlogicGrabber.h + ${CMAKE_SOURCE_DIR}/include/grabber/amlogic/AmlogicWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/AmlogicGrabber.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/AmlogicWrapper.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/Amvideocap.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/ion.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/meson_ion.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/IonBuffer.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic/IonBuffer.cpp +) target_link_libraries(amlogic-grabber hyperion - ${QT_LIBRARIES}) +) diff --git a/libsrc/grabber/audio/AudioGrabber.cpp b/libsrc/grabber/audio/AudioGrabber.cpp index 4f4eccbd..995a1b7c 100644 --- a/libsrc/grabber/audio/AudioGrabber.cpp +++ b/libsrc/grabber/audio/AudioGrabber.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/libsrc/grabber/audio/AudioGrabberLinux.cpp b/libsrc/grabber/audio/AudioGrabberLinux.cpp index 8938e043..944b317d 100644 --- a/libsrc/grabber/audio/AudioGrabberLinux.cpp +++ b/libsrc/grabber/audio/AudioGrabberLinux.cpp @@ -1,11 +1,31 @@ -#include +#include #include #include #include -typedef void* (*THREADFUNCPTR)(void*); +static void * AudioThreadRunner(void* params) +{ + AudioGrabberLinux* This = static_cast(params); + + Debug(This->getLog(), "Audio Thread Started"); + + snd_pcm_sframes_t framesAvailable = 0; + + while (This->_isRunning.load(std::memory_order_acquire)) + { + snd_pcm_wait(This->_captureDevice, 1000); + + if ((framesAvailable = snd_pcm_avail(This->_captureDevice)) > 0) + This->processAudioBuffer(framesAvailable); + + sched_yield(); + } + + Debug(This->getLog(), "Audio Thread Shutting Down"); + return nullptr; +} AudioGrabberLinux::AudioGrabberLinux() : AudioGrabber() @@ -121,7 +141,7 @@ bool AudioGrabberLinux::configureCaptureInterface() snd_pcm_close(_captureDevice); return false; } - + if ((error = snd_pcm_hw_params_set_access(_captureDevice, _captureDeviceConfig, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { Error(_log, "Failed to configure interleaved mode: %s", snd_strerror(error)); @@ -129,7 +149,7 @@ bool AudioGrabberLinux::configureCaptureInterface() snd_pcm_close(_captureDevice); return false; } - + if ((error = snd_pcm_hw_params_set_format(_captureDevice, _captureDeviceConfig, SND_PCM_FORMAT_S16_LE)) < 0) { Error(_log, "Failed to configure capture format: %s", snd_strerror(error)); @@ -169,7 +189,7 @@ bool AudioGrabberLinux::configureCaptureInterface() snd_pcm_close(_captureDevice); return false; } - + return true; } @@ -189,11 +209,6 @@ bool AudioGrabberLinux::start() _isRunning.store(true, std::memory_order_release); pthread_attr_t threadAttributes; - int threadPriority = 1; - - sched_param schedulerParameter; - schedulerParameter.sched_priority = threadPriority; - if (pthread_attr_init(&threadAttributes) != 0) { Debug(_log, "Failed to create thread attributes"); @@ -201,7 +216,7 @@ bool AudioGrabberLinux::start() return false; } - if (pthread_create(&_audioThread, &threadAttributes, static_cast(&AudioThreadRunner), static_cast(this)) != 0) + if (pthread_create(&_audioThread, &threadAttributes, &AudioThreadRunner, static_cast(this)) != 0) { Debug(_log, "Failed to create audio capture thread"); stop(); @@ -239,7 +254,7 @@ void AudioGrabberLinux::processAudioBuffer(snd_pcm_sframes_t frames) ssize_t bytes = snd_pcm_frames_to_bytes(_captureDevice, frames); int16_t * buffer = static_cast(calloc(static_cast(bytes / 2), sizeof(int16_t))); - + if (frames == 0) { buffer[0] = 0; @@ -293,25 +308,3 @@ QString AudioGrabberLinux::getDeviceName(const QString& devicePath) const return _deviceProperties.value(devicePath).name; } - -static void * AudioThreadRunner(void* params) -{ - AudioGrabberLinux* This = static_cast(params); - - Debug(This->getLog(), "Audio Thread Started"); - - snd_pcm_sframes_t framesAvailable = 0; - - while (This->_isRunning.load(std::memory_order_acquire)) - { - snd_pcm_wait(This->_captureDevice, 1000); - - if ((framesAvailable = snd_pcm_avail(This->_captureDevice)) > 0) - This->processAudioBuffer(framesAvailable); - - sched_yield(); - } - - Debug(This->getLog(), "Audio Thread Shutting Down"); - return nullptr; -} diff --git a/libsrc/grabber/audio/AudioGrabberWindows.cpp b/libsrc/grabber/audio/AudioGrabberWindows.cpp index 8a2228c3..d3d37597 100644 --- a/libsrc/grabber/audio/AudioGrabberWindows.cpp +++ b/libsrc/grabber/audio/AudioGrabberWindows.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -71,7 +71,7 @@ bool AudioGrabberWindows::configureCaptureInterface() notificationSize -= notificationSize % audioFormat.nBlockAlign; bufferCaptureSize = notificationSize * AUDIO_NOTIFICATION_COUNT; - + DSCBUFFERDESC bufferDesc; bufferDesc.dwSize = sizeof(DSCBUFFERDESC); bufferDesc.dwFlags = 0; @@ -80,7 +80,7 @@ bool AudioGrabberWindows::configureCaptureInterface() bufferDesc.lpwfxFormat = &audioFormat; bufferDesc.dwFXCount = 0; bufferDesc.lpDSCFXDesc = NULL; - + // Create Capture Device's Buffer LPDIRECTSOUNDCAPTUREBUFFER preBuffer; if (FAILED(recordingDevice->CreateCaptureBuffer(&bufferDesc, &preBuffer, NULL))) @@ -101,7 +101,7 @@ bool AudioGrabberWindows::configureCaptureInterface() } preBuffer->Release(); - + // Create Notifications LPDIRECTSOUNDNOTIFY8 notify; @@ -112,7 +112,7 @@ bool AudioGrabberWindows::configureCaptureInterface() recordingBuffer->Release(); return false; } - + // Create Events notificationEvent = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -133,11 +133,11 @@ bool AudioGrabberWindows::configureCaptureInterface() positionNotify[i].dwOffset = (notificationSize * i) + notificationSize - 1; positionNotify[i].hEventNotify = notificationEvent; } - + // Set Notifications notify->SetNotificationPositions(AUDIO_NOTIFICATION_COUNT, positionNotify); notify->Release(); - + return true; } @@ -162,12 +162,12 @@ bool AudioGrabberWindows::start() } Info(_log, "Capture audio from %s", QSTRING_CSTR(getDeviceName(_device))); - + if (!this->configureCaptureInterface()) { return false; } - + if (FAILED(recordingBuffer->Start(DSCBSTART_LOOPING))) { Error(_log, "Failed starting audio capture from '%s'", QSTRING_CSTR(getDeviceName(_device))); @@ -214,7 +214,7 @@ void AudioGrabberWindows::stop() { Error(_log, "Audio capture failed to stop: '%s'", QSTRING_CSTR(getDeviceName(_device))); } - + if (FAILED(recordingBuffer->Release())) { Error(_log, "Failed to release recording buffer: '%s'", QSTRING_CSTR(getDeviceName(_device))); @@ -306,7 +306,7 @@ void AudioGrabberWindows::processAudioBuffer() // Buffer wrapped around, read second position if (capturedAudio2 != NULL) - { + { bufferCapturePosition += capturedAudio2Length; bufferCapturePosition %= bufferCaptureSize; // Circular Buffer } @@ -318,13 +318,13 @@ void AudioGrabberWindows::processAudioBuffer() { CopyMemory(readBuffer + capturedAudioLength, capturedAudio2, capturedAudio2Length); } - + // Release Buffer Lock recordingBuffer->Unlock(capturedAudio, capturedAudioLength, capturedAudio2, capturedAudio2Length); - + // Process Audio Frame this->processAudioFrame(readBuffer, frameSize); - + delete[] readBuffer; } diff --git a/libsrc/grabber/audio/AudioWrapper.cpp b/libsrc/grabber/audio/AudioWrapper.cpp index 0dd624de..2c47a3b8 100644 --- a/libsrc/grabber/audio/AudioWrapper.cpp +++ b/libsrc/grabber/audio/AudioWrapper.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/libsrc/grabber/audio/CMakeLists.txt b/libsrc/grabber/audio/CMakeLists.txt index 714c5883..f60ee775 100644 --- a/libsrc/grabber/audio/CMakeLists.txt +++ b/libsrc/grabber/audio/CMakeLists.txt @@ -1,35 +1,38 @@ -# Define the current source locations -SET( CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber ) -SET( CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/audio ) - -if (WIN32) - add_definitions(-DUNICODE -D_UNICODE) - FILE ( GLOB AUDIO_GRABBER_SOURCES "${CURRENT_HEADER_DIR}/Audio*Windows.h" "${CURRENT_HEADER_DIR}/AudioGrabber.h" "${CURRENT_HEADER_DIR}/AudioWrapper.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*Windows.cpp" "${CURRENT_SOURCE_DIR}/AudioGrabber.cpp" "${CURRENT_SOURCE_DIR}/AudioWrapper.cpp") -elseif(${CMAKE_SYSTEM} MATCHES "Linux") - FILE ( GLOB AUDIO_GRABBER_SOURCES "${CURRENT_HEADER_DIR}/Audio*Linux.h" "${CURRENT_HEADER_DIR}/AudioGrabber.h" "${CURRENT_HEADER_DIR}/AudioWrapper.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*Linux.cpp" "${CURRENT_SOURCE_DIR}/AudioGrabber.cpp" "${CURRENT_SOURCE_DIR}/AudioWrapper.cpp") -elseif (APPLE) - #TODO - #FILE ( GLOB AUDIO_GRABBER_SOURCES "${CURRENT_HEADER_DIR}/Audio*Apple.h" "${CURRENT_HEADER_DIR}/AudioGrabber.h" "${CURRENT_HEADER_DIR}/AudioWrapper.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*Apple.cpp" "${CURRENT_SOURCE_DIR}/AudioGrabber.cpp" "${CURRENT_SOURCE_DIR}/AudioWrapper.cpp") +if(WIN32) + add_definitions(-DUNICODE -D_UNICODE) + set(AUDIO_GRABBER_SOURCES + ${CMAKE_SOURCE_DIR}/include/grabber/audio/AudioGrabberWindows.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/audio/AudioGrabberWindows.cpp + ) +elseif(CMAKE_HOST_UNIX AND NOT APPLE) + set(AUDIO_GRABBER_SOURCES + ${CMAKE_SOURCE_DIR}/include/grabber/audio/AudioGrabberLinux.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/audio/AudioGrabberLinux.cpp + ) endif() -add_library( audio-grabber ${AUDIO_GRABBER_SOURCES} ) +add_library(audio-grabber + ${CMAKE_SOURCE_DIR}/include/grabber/audio/AudioGrabber.h + ${CMAKE_SOURCE_DIR}/include/grabber/audio/AudioWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/audio/AudioGrabber.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/audio/AudioWrapper.cpp + ${AUDIO_GRABBER_SOURCES} +) -set(AUDIO_LIBS hyperion) - - -if (WIN32) - set(AUDIO_LIBS ${AUDIO_LIBS} DSound) -elseif(${CMAKE_SYSTEM} MATCHES "Linux") - find_package(ALSA REQUIRED) - if (ALSA_FOUND) - include_directories(${ALSA_INCLUDE_DIRS}) - set(AUDIO_LIBS ${AUDIO_LIBS} ${ALSA_LIBRARIES}) - endif(ALSA_FOUND) - - set(THREADS_PREFER_PTHREAD_FLAG ON) - find_package(Threads REQUIRED) - set(AUDIO_LIBS ${AUDIO_LIBS} Threads::Threads) # PRIVATE +if(WIN32) + set(AUDIO_LIBS DSound) +elseif(CMAKE_HOST_UNIX AND NOT APPLE) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(ALSA REQUIRED) + find_package(Threads REQUIRED) + set(AUDIO_LIBS ${ALSA_LIBRARIES} Threads::Threads) endif() +target_link_libraries(audio-grabber + hyperion + ${AUDIO_LIBS} +) -target_link_libraries(audio-grabber ${AUDIO_LIBS} ${QT_LIBRARIES}) +if(CMAKE_HOST_UNIX AND NOT APPLE) + target_include_directories(audio-grabber PUBLIC ${ALSA_INCLUDE_DIRS}) +endif() diff --git a/libsrc/grabber/directx/CMakeLists.txt b/libsrc/grabber/directx/CMakeLists.txt index 16db7dd2..3d86e897 100644 --- a/libsrc/grabber/directx/CMakeLists.txt +++ b/libsrc/grabber/directx/CMakeLists.txt @@ -1,14 +1,17 @@ -# Define the current source locations -SET( CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber ) -SET( CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/directx ) +find_package(DirectX9 REQUIRED) -include_directories(${DIRECTX9_INCLUDE_DIRS}) - -FILE ( GLOB DIRECTX_GRAB_SOURCES "${CURRENT_HEADER_DIR}/DirectX*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library( directx-grabber ${DIRECTX_GRAB_SOURCES} ) +add_library(directx-grabber + ${CMAKE_SOURCE_DIR}/include/grabber/directx/DirectXGrabber.h + ${CMAKE_SOURCE_DIR}/include/grabber/directx/DirectXWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/directx/DirectXGrabber.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/directx/DirectXWrapper.cpp +) target_link_libraries(directx-grabber - hyperion - ${DIRECTX9_LIBRARIES} + hyperion + ${DIRECTX9_LIBRARIES} +) + +target_include_directories(directx-grabber PUBLIC + ${DIRECTX9_INCLUDE_DIRS} ) diff --git a/libsrc/grabber/directx/DirectXGrabber.cpp b/libsrc/grabber/directx/DirectXGrabber.cpp index ae909888..24db4b4f 100644 --- a/libsrc/grabber/directx/DirectXGrabber.cpp +++ b/libsrc/grabber/directx/DirectXGrabber.cpp @@ -1,5 +1,5 @@ #include -#include +#include #pragma comment(lib, "d3d9.lib") #pragma comment(lib,"d3dx9.lib") diff --git a/libsrc/grabber/directx/DirectXWrapper.cpp b/libsrc/grabber/directx/DirectXWrapper.cpp index 3c4188e5..cddf19ce 100644 --- a/libsrc/grabber/directx/DirectXWrapper.cpp +++ b/libsrc/grabber/directx/DirectXWrapper.cpp @@ -1,4 +1,4 @@ -#include +#include DirectXWrapper::DirectXWrapper( int updateRate_Hz, int display, diff --git a/libsrc/grabber/dispmanx/CMakeLists.txt b/libsrc/grabber/dispmanx/CMakeLists.txt index 51292da1..93082b9c 100644 --- a/libsrc/grabber/dispmanx/CMakeLists.txt +++ b/libsrc/grabber/dispmanx/CMakeLists.txt @@ -1,5 +1,5 @@ # Find the BCM-package (VC control) -if( "${PLATFORM}" MATCHES rpi) +if("${PLATFORM}" MATCHES rpi) find_package(BCM) if(BCM_FOUND) add_definitions(-DBCM_FOUND) @@ -9,21 +9,20 @@ else() set(BCM_LIBRARY "") endif() -# Define the current source locations -set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx) +add_library(dispmanx-grabber + ${CMAKE_SOURCE_DIR}/include/grabber/dispmanx/DispmanxFrameGrabber.h + ${CMAKE_SOURCE_DIR}/include/grabber/dispmanx/DispmanxWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx/DispmanxFrameGrabber.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx/DispmanxWrapper.cpp +) -FILE ( GLOB DispmanxGrabberSOURCES "${CURRENT_HEADER_DIR}/Dispmanx*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(dispmanx-grabber ${DispmanxGrabberSOURCES}) add_definitions(-DBCM_LIBRARY="${BCM_LIBRARY}") +target_link_libraries(dispmanx-grabber + hyperion + ${CMAKE_DL_LIBS} +) + target_include_directories(dispmanx-grabber PUBLIC ${BCM_INCLUDE_DIR} ) - -target_link_libraries(dispmanx-grabber - hyperion - ${QT_LIBRARIES} - ${CMAKE_DL_LIBS} -) diff --git a/libsrc/grabber/dispmanx/DispmanxFrameGrabber.cpp b/libsrc/grabber/dispmanx/DispmanxFrameGrabber.cpp index b0a46fe4..d0486cdf 100644 --- a/libsrc/grabber/dispmanx/DispmanxFrameGrabber.cpp +++ b/libsrc/grabber/dispmanx/DispmanxFrameGrabber.cpp @@ -16,7 +16,7 @@ namespace { } //End of constants // Local includes -#include "grabber/DispmanxFrameGrabber.h" +#include "grabber/dispmanx/DispmanxFrameGrabber.h" DispmanxFrameGrabber::DispmanxFrameGrabber() : Grabber("DISPMANXGRABBER") diff --git a/libsrc/grabber/dispmanx/DispmanxWrapper.cpp b/libsrc/grabber/dispmanx/DispmanxWrapper.cpp index 6ffe3354..19fb1b02 100644 --- a/libsrc/grabber/dispmanx/DispmanxWrapper.cpp +++ b/libsrc/grabber/dispmanx/DispmanxWrapper.cpp @@ -1,4 +1,4 @@ -#include +#include DispmanxWrapper::DispmanxWrapper( int updateRate_Hz, int pixelDecimation diff --git a/libsrc/grabber/framebuffer/CMakeLists.txt b/libsrc/grabber/framebuffer/CMakeLists.txt index af1368ff..bcaf4675 100644 --- a/libsrc/grabber/framebuffer/CMakeLists.txt +++ b/libsrc/grabber/framebuffer/CMakeLists.txt @@ -1,11 +1,10 @@ -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/framebuffer) - -FILE ( GLOB FramebufferGrabberSOURCES "${CURRENT_HEADER_DIR}/Framebuffer*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(framebuffer-grabber ${FramebufferGrabberSOURCES} ) +add_library(framebuffer-grabber + ${CMAKE_SOURCE_DIR}/include/grabber/framebuffer/FramebufferFrameGrabber.h + ${CMAKE_SOURCE_DIR}/include/grabber/framebuffer/FramebufferWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/framebuffer/FramebufferWrapper.cpp +) target_link_libraries(framebuffer-grabber hyperion - ${QT_LIBRARIES}) +) diff --git a/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp b/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp index 554f8166..c4129341 100644 --- a/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp +++ b/libsrc/grabber/framebuffer/FramebufferFrameGrabber.cpp @@ -28,7 +28,7 @@ const char DISCOVERY_FILEPATTERN[] = "fb?"; } //End of constants // Local includes -#include +#include FramebufferFrameGrabber::FramebufferFrameGrabber(const QString & device) : Grabber("FRAMEBUFFERGRABBER") diff --git a/libsrc/grabber/framebuffer/FramebufferWrapper.cpp b/libsrc/grabber/framebuffer/FramebufferWrapper.cpp index 7d99f527..9cb40046 100644 --- a/libsrc/grabber/framebuffer/FramebufferWrapper.cpp +++ b/libsrc/grabber/framebuffer/FramebufferWrapper.cpp @@ -1,4 +1,4 @@ -#include +#include FramebufferWrapper::FramebufferWrapper( int updateRate_Hz, const QString & device, diff --git a/libsrc/grabber/osx/CMakeLists.txt b/libsrc/grabber/osx/CMakeLists.txt index 3a5690c6..7d18e8d3 100644 --- a/libsrc/grabber/osx/CMakeLists.txt +++ b/libsrc/grabber/osx/CMakeLists.txt @@ -1,11 +1,10 @@ -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/osx) - -FILE ( GLOB OsxGrabberSOURCES "${CURRENT_HEADER_DIR}/Osx*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(osx-grabber ${OsxGrabberSOURCES} ) +add_library(osx-grabber + ${CMAKE_SOURCE_DIR}/include/grabber/osx/OsxFrameGrabber.h + ${CMAKE_SOURCE_DIR}/include/grabber/osx/OsxWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/osx/OsxFrameGrabber.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/osx/OsxWrapper.cpp +) target_link_libraries(osx-grabber hyperion - ${QT_LIBRARIES}) +) diff --git a/libsrc/grabber/osx/OsxFrameGrabber.cpp b/libsrc/grabber/osx/OsxFrameGrabber.cpp index 54ea28f7..0e42e529 100644 --- a/libsrc/grabber/osx/OsxFrameGrabber.cpp +++ b/libsrc/grabber/osx/OsxFrameGrabber.cpp @@ -3,7 +3,7 @@ #include // Local includes -#include +#include //Qt #include diff --git a/libsrc/grabber/osx/OsxFrameGrabberMock.cpp b/libsrc/grabber/osx/OsxFrameGrabberMock.cpp deleted file mode 100644 index 2bcab484..00000000 --- a/libsrc/grabber/osx/OsxFrameGrabberMock.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef __APPLE__ -#include - -unsigned __osx_frame_counter = 0; -const int __screenWidth = 800; -const int __screenHeight = 600; - -CGError CGGetActiveDisplayList(uint32_t maxDisplays, CGDirectDisplayID *activeDisplays, uint32_t *displayCount) -{ - if (maxDisplays == 0 || activeDisplays == nullptr) - { - *displayCount = 2; - } - else - { - displayCount = &maxDisplays; - if (activeDisplays != nullptr) - { - for (CGDirectDisplayID i = 0; i < maxDisplays; ++i) - { - activeDisplays[i] = i; - } - } - else - { - return kCGErrorFailure; - } - } - return kCGErrorSuccess; -} - -CGImageRef CGDisplayCreateImage(CGDirectDisplayID display) -{ - CGImageRef image = new CGImage(__screenWidth / (display+1), __screenHeight / (display+1)); - - return image; -} - -void CGImageRelease(CGImageRef image) -{ - delete image; -} - -CGImageRef CGImageGetDataProvider(CGImageRef image) -{ - __osx_frame_counter++; - if (__osx_frame_counter > 100) - { - __osx_frame_counter = 0; - } - - ColorRgb color[4] = {ColorRgb::RED, ColorRgb::BLUE, ColorRgb::GREEN, ColorRgb::WHITE}; - if (__osx_frame_counter < 25) - { - color[0] = ColorRgb::WHITE; - color[1] = ColorRgb::RED; - color[2] = ColorRgb::BLUE; - color[3] = ColorRgb::GREEN; - } - else if(__osx_frame_counter < 50) - { - color[1] = ColorRgb::WHITE; - color[2] = ColorRgb::RED; - color[3] = ColorRgb::BLUE; - color[0] = ColorRgb::GREEN; - } - else if(__osx_frame_counter < 75) - { - color[2] = ColorRgb::WHITE; - color[3] = ColorRgb::RED; - color[0] = ColorRgb::BLUE; - color[1] = ColorRgb::GREEN; - } - unsigned w = image->width(); - unsigned h = image->height(); - - for (unsigned y=0; y= h/2) id = 2; - if (x >= w/2 && y < h/2) id = 3; - - image->memptr()[y*w + x] = color[id]; - } - } - - return image; -} - -CFDataRef CGDataProviderCopyData(CGImageRef image) -{ - const unsigned indexMax = image->width() * image->height() * CGImageGetBitsPerPixel(image); - CFDataRef data = new CFData[indexMax]; - int lineLength = CGImageGetBytesPerRow(image); - - for (unsigned y=0; yheight(); y++) - { - for (unsigned x=0; xwidth(); x++) - { - int index = lineLength * y + x * CGImageGetBitsPerPixel(image); - - data[index ] = (*image)(x,y).blue; - data[index+1] = (*image)(x,y).green; - data[index+2] = (*image)(x,y).red; - data[index+3] = 0; - } - } - return data; -} - -unsigned char* CFDataGetBytePtr(CFDataRef imgData) -{ - return imgData; -} - -unsigned CGImageGetWidth(CGImageRef image) -{ - return image->width(); -} - -unsigned CGImageGetHeight(CGImageRef image) -{ - return image->height(); -} - -unsigned CGImageGetBytesPerRow(CGImageRef image) -{ - return image->width()*CGImageGetBitsPerPixel(image); -} - -unsigned CGImageGetBitsPerPixel(CGImageRef) -{ - return 4; -} - -void CFRelease(CFDataRef imgData) -{ - delete imgData; -} - -CGDisplayModeRef CGDisplayCopyDisplayMode(CGDirectDisplayID display) -{ - return nullptr; -} -CGRect CGDisplayBounds(CGDirectDisplayID display) -{ - CGRect rect; - rect.size.width = __screenWidth / (display+1); - rect.size.height = __screenHeight / (display+1); - return rect; -} -void CGDisplayModeRelease(CGDisplayModeRef mode) -{ -} - -#endif diff --git a/libsrc/grabber/osx/OsxWrapper.cpp b/libsrc/grabber/osx/OsxWrapper.cpp index 724be2fa..408f7a69 100644 --- a/libsrc/grabber/osx/OsxWrapper.cpp +++ b/libsrc/grabber/osx/OsxWrapper.cpp @@ -1,4 +1,4 @@ -#include +#include OsxWrapper::OsxWrapper( int updateRate_Hz, int display, diff --git a/libsrc/grabber/qt/CMakeLists.txt b/libsrc/grabber/qt/CMakeLists.txt index 1b0111de..6ece8bbc 100644 --- a/libsrc/grabber/qt/CMakeLists.txt +++ b/libsrc/grabber/qt/CMakeLists.txt @@ -1,13 +1,10 @@ -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/qt) - - -FILE ( GLOB QT_GRAB_SOURCES "${CURRENT_HEADER_DIR}/Qt*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(qt-grabber ${QT_GRAB_SOURCES} ) +add_library(qt-grabber + ${CMAKE_SOURCE_DIR}/include/grabber/qt/QtGrabber.h + ${CMAKE_SOURCE_DIR}/include/grabber/qt/QtWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/qt/QtGrabber.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/qt/QtWrapper.cpp +) target_link_libraries(qt-grabber hyperion - ${QT_LIBRARIES} ) diff --git a/libsrc/grabber/qt/QtGrabber.cpp b/libsrc/grabber/qt/QtGrabber.cpp index cb6e0c5c..3d2cd51c 100644 --- a/libsrc/grabber/qt/QtGrabber.cpp +++ b/libsrc/grabber/qt/QtGrabber.cpp @@ -1,5 +1,5 @@ // proj -#include +#include // qt #include @@ -226,7 +226,7 @@ int QtGrabber::grabFrame(Image& image) QPixmap originalPixmap = grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max); #else QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max); -#endif +#endif if (originalPixmap.isNull()) { rc = -1; diff --git a/libsrc/grabber/qt/QtWrapper.cpp b/libsrc/grabber/qt/QtWrapper.cpp index 90cb489b..689f25c5 100644 --- a/libsrc/grabber/qt/QtWrapper.cpp +++ b/libsrc/grabber/qt/QtWrapper.cpp @@ -1,4 +1,4 @@ -#include +#include QtWrapper::QtWrapper( int updateRate_Hz, int display, diff --git a/libsrc/grabber/video/CMakeLists.txt b/libsrc/grabber/video/CMakeLists.txt index 43a0e580..c4c2f896 100644 --- a/libsrc/grabber/video/CMakeLists.txt +++ b/libsrc/grabber/video/CMakeLists.txt @@ -1,33 +1,38 @@ # Common cmake definition for external video grabber -# Add Turbo JPEG library -if (ENABLE_V4L2 OR ENABLE_MF) - find_package(TurboJPEG) - if (TURBOJPEG_FOUND) - add_definitions(-DHAVE_TURBO_JPEG) - message( STATUS "Using Turbo JPEG library: ${TurboJPEG_LIBRARY}") - include_directories(${TurboJPEG_INCLUDE_DIRS}) - else () - message( STATUS "Turbo JPEG library not found, MJPEG camera format won't work.") - endif () -endif() +set(MF-grabber mediafoundation) +set(V4L2-grabber v4l2) -# Define the wrapper/header/source locations and collect them -SET(WRAPPER_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/video) -SET(HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -if (ENABLE_MF) +if(ENABLE_MF) project(mf-grabber) - SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/video/mediafoundation) - FILE (GLOB SOURCES "${WRAPPER_DIR}/*.cpp" "${HEADER_DIR}/Video*.h" "${HEADER_DIR}/MF*.h" "${HEADER_DIR}/Encoder*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp") + set(grabber_project MF) + set(MediaFoundationSourceReaderCallBack ${CMAKE_SOURCE_DIR}/libsrc/grabber/video/mediafoundation/MFSourceReaderCB.h) elseif(ENABLE_V4L2) project(v4l2-grabber) - SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/video/v4l2) - FILE (GLOB SOURCES "${WRAPPER_DIR}/*.cpp" "${HEADER_DIR}/Video*.h" "${HEADER_DIR}/V4L2*.h" "${HEADER_DIR}/Encoder*.h" "${CURRENT_SOURCE_DIR}/*.cpp") + set(grabber_project V4L2) endif() -add_library(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} hyperion ${QT_LIBRARIES}) +add_library(${PROJECT_NAME} + ${CMAKE_SOURCE_DIR}/include/grabber/video/EncoderThread.h + ${CMAKE_SOURCE_DIR}/include/grabber/video/VideoWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/video/EncoderThread.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/video/VideoWrapper.cpp + ${CMAKE_SOURCE_DIR}/include/grabber/video/${${grabber_project}-grabber}/${grabber_project}Grabber.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/video/${${grabber_project}-grabber}/${grabber_project}Grabber.cpp + ${MediaFoundationSourceReaderCallBack} +) -if(TURBOJPEG_FOUND) - target_link_libraries(${PROJECT_NAME} ${TurboJPEG_LIBRARY}) +target_link_libraries(${PROJECT_NAME} hyperion) + +# Add Turbo JPEG library +if(ENABLE_V4L2 OR ENABLE_MF) + find_package(TurboJPEG) + if(TURBOJPEG_FOUND) + add_definitions(-DHAVE_TURBO_JPEG) + message(STATUS "Using Turbo JPEG library: ${TurboJPEG_LIBRARY}") + target_link_libraries(${PROJECT_NAME} ${TurboJPEG_LIBRARY}) + target_include_directories(${PROJECT_NAME} PUBLIC ${TurboJPEG_INCLUDE_DIRS}) + else () + message(STATUS "Turbo JPEG library not found, MJPEG camera format won't work.") + endif() endif() diff --git a/libsrc/grabber/video/EncoderThread.cpp b/libsrc/grabber/video/EncoderThread.cpp index 1de36ff7..3555bb9f 100644 --- a/libsrc/grabber/video/EncoderThread.cpp +++ b/libsrc/grabber/video/EncoderThread.cpp @@ -1,4 +1,4 @@ -#include "grabber/EncoderThread.h" +#include "grabber/video/EncoderThread.h" #include diff --git a/libsrc/grabber/video/VideoWrapper.cpp b/libsrc/grabber/video/VideoWrapper.cpp index 7a3ed201..3b95f561 100644 --- a/libsrc/grabber/video/VideoWrapper.cpp +++ b/libsrc/grabber/video/VideoWrapper.cpp @@ -1,6 +1,6 @@ #include -#include +#include // qt includes #include diff --git a/libsrc/grabber/video/mediafoundation/MFGrabber.cpp b/libsrc/grabber/video/mediafoundation/MFGrabber.cpp index 5e67e6ef..ca2b6a35 100644 --- a/libsrc/grabber/video/mediafoundation/MFGrabber.cpp +++ b/libsrc/grabber/video/mediafoundation/MFGrabber.cpp @@ -1,5 +1,5 @@ #include "MFSourceReaderCB.h" -#include "grabber/MFGrabber.h" +#include "grabber/video/mediafoundation/MFGrabber.h" // Constants namespace { const bool verbose = false; } @@ -537,7 +537,7 @@ void MFGrabber::process_image(const void *frameImageBuffer, int size) Error(_log, "Frame too small: %d != %d", size, _frameByteSize); else if (_threadManager != nullptr) { - for (unsigned long i = 0; i < _threadManager->_threadCount; i++) + for (int i = 0; i < _threadManager->_threadCount; i++) { if (!_threadManager->_threads[i]->isBusy()) { diff --git a/libsrc/grabber/video/mediafoundation/MFSourceReaderCB.h b/libsrc/grabber/video/mediafoundation/MFSourceReaderCB.h index 8cb10ba4..2bcef437 100644 --- a/libsrc/grabber/video/mediafoundation/MFSourceReaderCB.h +++ b/libsrc/grabber/video/mediafoundation/MFSourceReaderCB.h @@ -19,7 +19,7 @@ #pragma comment (lib, "strmiids.lib") #pragma comment (lib, "wmcodecdspuuid.lib") -#include +#include #define SAFE_RELEASE(x) if(x) { x->Release(); x = nullptr; } diff --git a/libsrc/grabber/video/v4l2/V4L2Grabber.cpp b/libsrc/grabber/video/v4l2/V4L2Grabber.cpp index 8f504464..cd0d273f 100644 --- a/libsrc/grabber/video/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/video/v4l2/V4L2Grabber.cpp @@ -23,7 +23,7 @@ #include #include -#include "grabber/V4L2Grabber.h" +#include "grabber/video/v4l2/V4L2Grabber.h" #define CLEAR(x) memset(&(x), 0, sizeof(x)) diff --git a/libsrc/grabber/x11/CMakeLists.txt b/libsrc/grabber/x11/CMakeLists.txt index 08183aff..d5cd3831 100644 --- a/libsrc/grabber/x11/CMakeLists.txt +++ b/libsrc/grabber/x11/CMakeLists.txt @@ -1,24 +1,23 @@ -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/x11) - -# Find X11 find_package(X11 REQUIRED) -include_directories( ${X11_INCLUDES} ) - -if(APPLE) - include_directories("/opt/X11/include") -endif(APPLE) - -FILE ( GLOB X11_SOURCES "${CURRENT_HEADER_DIR}/X11*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(x11-grabber ${X11_SOURCES} ) +add_library(x11-grabber + ${CMAKE_SOURCE_DIR}/include/grabber/x11/X11Grabber.h + ${CMAKE_SOURCE_DIR}/include/grabber/x11/X11Wrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/x11/X11Grabber.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/x11/X11Wrapper.cpp +) target_link_libraries(x11-grabber hyperion ${X11_LIBRARIES} ${X11_Xrandr_LIB} ${X11_Xrender_LIB} - ${QT_LIBRARIES} +) + +if(APPLE) + list(APPEND X11_INCLUDES "/opt/X11/include") +endif() + +target_include_directories(x11-grabber PUBLIC + ${X11_INCLUDES} ) diff --git a/libsrc/grabber/x11/X11Grabber.cpp b/libsrc/grabber/x11/X11Grabber.cpp index 46c7faa4..cf24eea2 100644 --- a/libsrc/grabber/x11/X11Grabber.cpp +++ b/libsrc/grabber/x11/X11Grabber.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/libsrc/grabber/x11/X11Wrapper.cpp b/libsrc/grabber/x11/X11Wrapper.cpp index a453fc19..a7e36b71 100644 --- a/libsrc/grabber/x11/X11Wrapper.cpp +++ b/libsrc/grabber/x11/X11Wrapper.cpp @@ -1,4 +1,4 @@ -#include +#include X11Wrapper::X11Wrapper( int updateRate_Hz, int pixelDecimation, diff --git a/libsrc/grabber/xcb/CMakeLists.txt b/libsrc/grabber/xcb/CMakeLists.txt index 97f1ee91..d353f2bd 100644 --- a/libsrc/grabber/xcb/CMakeLists.txt +++ b/libsrc/grabber/xcb/CMakeLists.txt @@ -1,23 +1,19 @@ -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/xcb) - find_package(XCB COMPONENTS SHM IMAGE RENDER RANDR REQUIRED) -include_directories(${XCB_INCLUDE_DIRS}) - -FILE (GLOB XCB_SOURCES "${CURRENT_HEADER_DIR}/Xcb*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(xcb-grabber ${XCB_SOURCES}) +add_library(xcb-grabber + ${CMAKE_SOURCE_DIR}/include/grabber/xcb/XcbGrabber.h + ${CMAKE_SOURCE_DIR}/include/grabber/xcb/XcbWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/xcb/XcbCommandExecutor.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/xcb/XcbCommands.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/xcb/XcbGrabber.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/xcb/XcbWrapper.cpp +) target_link_libraries(xcb-grabber hyperion ${XCB_LIBRARIES} - ${QT_LIBRARIES} ) -if (NOT APPLE) - target_link_libraries( - xcb-grabber - ) -endif() +target_include_directories(xcb-grabber PUBLIC + ${XCB_INCLUDE_DIRS} +) diff --git a/libsrc/grabber/xcb/XcbGrabber.cpp b/libsrc/grabber/xcb/XcbGrabber.cpp index a5fc97f9..bf58770d 100644 --- a/libsrc/grabber/xcb/XcbGrabber.cpp +++ b/libsrc/grabber/xcb/XcbGrabber.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include "XcbCommands.h" #include "XcbCommandExecutor.h" diff --git a/libsrc/grabber/xcb/XcbWrapper.cpp b/libsrc/grabber/xcb/XcbWrapper.cpp index 339cb4e8..129217e1 100644 --- a/libsrc/grabber/xcb/XcbWrapper.cpp +++ b/libsrc/grabber/xcb/XcbWrapper.cpp @@ -1,4 +1,4 @@ -#include +#include XcbWrapper::XcbWrapper( int updateRate_Hz, int pixelDecimation, diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index 3a880a91..77600086 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -1,21 +1,50 @@ - -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) - -if(ENABLE_FLATBUF_SERVER) -include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver -) -endif() - -FILE ( GLOB Hyperion_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -SET(Hyperion_RESOURCES ${CURRENT_SOURCE_DIR}/resource.qrc) - add_library(hyperion - ${Hyperion_SOURCES} - ${Hyperion_RESOURCES} + # Authorization Manager + ${CMAKE_SOURCE_DIR}/include/hyperion/AuthManager.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/AuthManager.cpp + # Background Effect Handler + ${CMAKE_SOURCE_DIR}/include/hyperion/BGEffectHandler.h + # Capture Control class + ${CMAKE_SOURCE_DIR}/include/hyperion/CaptureCont.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/CaptureCont.cpp + # Color Adjustment + ${CMAKE_SOURCE_DIR}/include/hyperion/ColorAdjustment.h + # Component Register + ${CMAKE_SOURCE_DIR}/include/hyperion/ComponentRegister.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/ComponentRegister.cpp + # Grabber/Wrapper classes + ${CMAKE_SOURCE_DIR}/include/hyperion/Grabber.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/Grabber.cpp + ${CMAKE_SOURCE_DIR}/include/hyperion/GrabberWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/GrabberWrapper.cpp + # Hyperion + Resources + ${CMAKE_SOURCE_DIR}/include/hyperion/Hyperion.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/Hyperion.cpp + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/resource.qrc + # Instance Manager + ${CMAKE_SOURCE_DIR}/include/hyperion/HyperionIManager.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/HyperionIManager.cpp + # Image Processor + ${CMAKE_SOURCE_DIR}/include/hyperion/ImageProcessor.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/ImageProcessor.cpp + # ImageToLedsMap class + ${CMAKE_SOURCE_DIR}/include/hyperion/ImageToLedsMap.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/ImageToLedsMap.cpp + # Led String + ${CMAKE_SOURCE_DIR}/include/hyperion/LedString.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/LedString.cpp + # Linear Color Smoothing + ${CMAKE_SOURCE_DIR}/include/hyperion/LinearColorSmoothing.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/LinearColorSmoothing.cpp + # Led Color Transform + ${CMAKE_SOURCE_DIR}/include/hyperion/MultiColorAdjustment.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/MultiColorAdjustment.cpp + # Priority Muxer + ${CMAKE_SOURCE_DIR}/include/hyperion/PriorityMuxer.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/PriorityMuxer.cpp + # Settings Manager + ${CMAKE_SOURCE_DIR}/include/hyperion/SettingsManager.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/SettingsManager.cpp ) target_link_libraries(hyperion @@ -23,7 +52,6 @@ target_link_libraries(hyperion hyperion-utils leddevice database - ${QT_LIBRARIES} ) if(ENABLE_BOBLIGHT_SERVER) diff --git a/libsrc/jsonserver/CMakeLists.txt b/libsrc/jsonserver/CMakeLists.txt index 30ca1373..b8b3c5b7 100644 --- a/libsrc/jsonserver/CMakeLists.txt +++ b/libsrc/jsonserver/CMakeLists.txt @@ -1,20 +1,15 @@ - -# Define the current source locations -set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/jsonserver) -set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/jsonserver) - -FILE ( GLOB JsonServer_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -add_library(jsonserver ${JsonServer_SOURCES} ) +add_library(jsonserver + ${CMAKE_SOURCE_DIR}/include/jsonserver/JsonServer.h + ${CMAKE_SOURCE_DIR}/libsrc/jsonserver/JsonServer.cpp + ${CMAKE_SOURCE_DIR}/libsrc/jsonserver/JsonClientConnection.h + ${CMAKE_SOURCE_DIR}/libsrc/jsonserver/JsonClientConnection.cpp +) target_link_libraries(jsonserver hyperion-api hyperion - Qt${QT_VERSION_MAJOR}::Network - Qt${QT_VERSION_MAJOR}::Gui ) if(ENABLE_MDNS) target_link_libraries(jsonserver mdns) endif() - diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index 233bb9fd..259ebf14 100644 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -1,13 +1,13 @@ # Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/leddevice) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/leddevice) +set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/leddevice) +set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/leddevice) -if ( ENABLE_DEV_NETWORK ) +if(ENABLE_DEV_NETWORK) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Network REQUIRED) endif() -if ( ENABLE_DEV_SERIAL ) +if(ENABLE_DEV_SERIAL) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS SerialPort REQUIRED) endif() @@ -21,7 +21,7 @@ include_directories( dev_tinker ) -FILE ( GLOB Leddevice_SOURCES +file (GLOB Leddevice_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" @@ -29,49 +29,48 @@ FILE ( GLOB Leddevice_SOURCES "${CURRENT_SOURCE_DIR}/dev_other/*.cpp" ) -if ( ENABLE_OSX OR WIN32 ) +if(ENABLE_OSX OR WIN32) list(REMOVE_ITEM Leddevice_SOURCES "${CURRENT_SOURCE_DIR}/dev_other/LedDevicePiBlaster.h") list(REMOVE_ITEM Leddevice_SOURCES "${CURRENT_SOURCE_DIR}/dev_other/LedDevicePiBlaster.cpp") endif() -if ( ENABLE_DEV_NETWORK ) - FILE ( GLOB Leddevice_NETWORK_SOURCES "${CURRENT_SOURCE_DIR}/dev_net/*.h" "${CURRENT_SOURCE_DIR}/dev_net/*.cpp") +if(ENABLE_DEV_NETWORK) + file (GLOB Leddevice_NETWORK_SOURCES "${CURRENT_SOURCE_DIR}/dev_net/*.h" "${CURRENT_SOURCE_DIR}/dev_net/*.cpp") endif() -if ( ENABLE_DEV_SERIAL ) - FILE ( GLOB Leddevice_SERIAL_SOURCES "${CURRENT_SOURCE_DIR}/dev_serial/*.h" "${CURRENT_SOURCE_DIR}/dev_serial/*.cpp") +if(ENABLE_DEV_SERIAL) + file (GLOB Leddevice_SERIAL_SOURCES "${CURRENT_SOURCE_DIR}/dev_serial/*.h" "${CURRENT_SOURCE_DIR}/dev_serial/*.cpp") endif() -if ( ENABLE_DEV_SPI ) - FILE ( GLOB Leddevice_SPI_SOURCES "${CURRENT_SOURCE_DIR}/dev_spi/*.h" "${CURRENT_SOURCE_DIR}/dev_spi/*.cpp") +if(ENABLE_DEV_SPI) + file (GLOB Leddevice_SPI_SOURCES "${CURRENT_SOURCE_DIR}/dev_spi/*.h" "${CURRENT_SOURCE_DIR}/dev_spi/*.cpp") endif() -if ( ENABLE_DEV_TINKERFORGE ) - FILE ( GLOB Leddevice_TINKER_SOURCES "${CURRENT_SOURCE_DIR}/dev_tinker/*.h" "${CURRENT_SOURCE_DIR}/dev_tinker/*.cpp") +if(ENABLE_DEV_TINKERFORGE) + file (GLOB Leddevice_TINKER_SOURCES "${CURRENT_SOURCE_DIR}/dev_tinker/*.h" "${CURRENT_SOURCE_DIR}/dev_tinker/*.cpp") endif() -if ( ENABLE_DEV_USB_HID ) +if(ENABLE_DEV_USB_HID) find_package(libusb-1.0 REQUIRED) include_directories( ${CMAKE_SOURCE_DIR}/include/hidapi ${LIBUSB_1_INCLUDE_DIRS} ) - FILE ( GLOB Leddevice_USB_HID_SOURCES "${CURRENT_SOURCE_DIR}/dev_hid/*.h" "${CURRENT_SOURCE_DIR}/dev_hid/*.cpp") + file (GLOB Leddevice_USB_HID_SOURCES "${CURRENT_SOURCE_DIR}/dev_hid/*.h" "${CURRENT_SOURCE_DIR}/dev_hid/*.cpp") endif() -if ( ENABLE_DEV_WS281XPWM ) - include_directories(../../dependencies/external/rpi_ws281x) - FILE ( GLOB Leddevice_PWM_SOURCES "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.h" "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.cpp") +if(ENABLE_DEV_WS281XPWM) + file (GLOB Leddevice_PWM_SOURCES "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.h" "${CURRENT_SOURCE_DIR}/dev_rpi_pwm/*.cpp") endif() -set(LedDevice_RESOURCES ${CURRENT_SOURCE_DIR}/LedDeviceSchemas.qrc ) +set(LedDevice_RESOURCES ${CURRENT_SOURCE_DIR}/LedDeviceSchemas.qrc) -SET( Leddevice_SOURCES +set(Leddevice_SOURCES ${Leddevice_SOURCES} ${LedDevice_RESOURCES} ${Leddevice_NETWORK_SOURCES} ${Leddevice_PWM_SOURCES} - ${Leddevice_SERIAL_SOURCES} + ${Leddevice_SERIAL_SOURCES} ${Leddevice_SPI_SOURCES} ${Leddevice_TINKER_SOURCES} ${Leddevice_USB_HID_SOURCES} @@ -79,20 +78,20 @@ SET( Leddevice_SOURCES # auto generate header file that include all available leddevice headers # auto generate cpp file for register() calls -FILE ( WRITE "${CMAKE_BINARY_DIR}/LedDevice_headers.h" "#pragma once\n\n//this file is autogenerated, don't touch it\n\n" ) -FILE ( WRITE "${CMAKE_BINARY_DIR}/LedDevice_register.cpp" "//this file is autogenerated, don't touch it\n\n" ) -FOREACH( f ${Leddevice_SOURCES} ) - # MESSAGE (STATUS "Add led device: ${f}") - if ( "${f}" MATCHES "dev_.*/Led.evice.+h$" ) +file (WRITE "${CMAKE_BINARY_DIR}/LedDevice_headers.h" "#pragma once\n\n//this file is autogenerated, don't touch it\n\n") +file (WRITE "${CMAKE_BINARY_DIR}/LedDevice_register.cpp" "//this file is autogenerated, don't touch it\n\n") +foreach(f ${Leddevice_SOURCES}) + # message (STATUS "Add led device: ${f}") + if("${f}" MATCHES "dev_.*/Led.evice.+h$") GET_FILENAME_COMPONENT(fname ${f} NAME) - FILE ( APPEND "${CMAKE_BINARY_DIR}/LedDevice_headers.h" "#include \"${fname}\"\n" ) - STRING( SUBSTRING ${fname} 9 -1 dname) - STRING( REPLACE ".h" "" dname "${dname}" ) - FILE ( APPEND "${CMAKE_BINARY_DIR}/LedDevice_register.cpp" "REGISTER(${dname});\n" ) + file (APPEND "${CMAKE_BINARY_DIR}/LedDevice_headers.h" "#include \"${fname}\"\n") + string(SUBSTRING ${fname} 9 -1 dname) + string(REPLACE ".h" "" dname "${dname}") + file (APPEND "${CMAKE_BINARY_DIR}/LedDevice_register.cpp" "REGISTER(${dname});\n") endif() -ENDFOREACH() +endforeach() -add_library(leddevice ${CMAKE_BINARY_DIR}/LedDevice_headers.h ${Leddevice_SOURCES} ) +add_library(leddevice ${CMAKE_BINARY_DIR}/LedDevice_headers.h ${Leddevice_SOURCES}) target_link_libraries(leddevice hyperion @@ -106,19 +105,19 @@ endif() if(ENABLE_DEV_NETWORK) target_link_libraries(leddevice Qt${QT_VERSION_MAJOR}::Network ssdp) - - if (NOT DEFAULT_USE_SYSTEM_MBEDTLS_LIBS) - if (MBEDTLS_LIBRARIES) + + if(NOT DEFAULT_USE_SYSTEM_MBEDTLS_LIBS) + if(MBEDTLS_LIBRARIES) include_directories(${MBEDTLS_INCLUDE_DIR}) target_link_libraries(leddevice ${MBEDTLS_LIBRARIES}) target_include_directories(leddevice PRIVATE ${MBEDTLS_INCLUDE_DIR}) endif (MBEDTLS_LIBRARIES) - endif () + endif() string(REGEX MATCH "[0-9]+|-([A-Za-z0-9_.]+)" MBEDTLS_MAJOR ${MBEDTLS_VERSION}) - if (MBEDTLS_MAJOR EQUAL "3") + if(MBEDTLS_MAJOR EQUAL "3") target_compile_definitions(leddevice PRIVATE USE_MBEDTLS3) - if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") target_compile_features(leddevice PRIVATE cxx_std_20) endif() endif() @@ -133,10 +132,11 @@ if(ENABLE_DEV_TINKERFORGE) endif() if(ENABLE_DEV_WS281XPWM) + target_include_directories(leddevice PUBLIC "${CMAKE_SOURCE_DIR}/dependencies/external/rpi_ws281x") target_link_libraries(leddevice ws281x) endif() -if (ENABLE_DEV_USB_HID) +if(ENABLE_DEV_USB_HID) if(APPLE) target_link_libraries(leddevice ${LIBUSB_1_LIBRARIES} hidapi-mac) else() @@ -150,17 +150,18 @@ if (ENABLE_DEV_USB_HID) #include int main() { - struct timespec t; - return clock_gettime(CLOCK_REALTIME, &t); + struct timespec t; + return clock_gettime(CLOCK_REALTIME, &t); } " GLIBC_HAS_CLOCK_GETTIME) - IF(NOT GLIBC_HAS_CLOCK_GETTIME) + if(NOT GLIBC_HAS_CLOCK_GETTIME) target_link_libraries(leddevice rt) endif() endif() endif() if(ENABLE_MDNS) - target_link_libraries(leddevice mdns) + + target_link_libraries(leddevice mdns) endif() diff --git a/libsrc/mdns/CMakeLists.txt b/libsrc/mdns/CMakeLists.txt index 4fdd5ed5..58bd4bcf 100644 --- a/libsrc/mdns/CMakeLists.txt +++ b/libsrc/mdns/CMakeLists.txt @@ -1,16 +1,13 @@ - -# Define the current source locations -set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/mdns) -set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/mdns) - -FILE ( GLOB MDNS_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) -add_library(mdns ${MDNS_SOURCES}) - -include_directories(${QMDNS_INCLUDE_DIR}) - -target_link_libraries(mdns -hyperion-utils -${QMDNS_LIBRARIES} +add_library(mdns + ${CMAKE_SOURCE_DIR}/include/mdns/MdnsServiceRegister.h + ${CMAKE_SOURCE_DIR}/include/mdns/MdnsBrowser.h + ${CMAKE_SOURCE_DIR}/include/mdns/MdnsProvider.h + ${CMAKE_SOURCE_DIR}/libsrc/mdns/MdnsBrowser.cpp + ${CMAKE_SOURCE_DIR}/libsrc/mdns/MdnsProvider.cpp ) -target_include_directories(mdns PUBLIC ${QMDNS_INCLUDE_DIR}) +target_link_libraries(mdns + hyperion + qmdnsengine + $<$:bcrypt.lib> +) diff --git a/libsrc/protoserver/CMakeLists.txt b/libsrc/protoserver/CMakeLists.txt index 5fefa7ef..a463f53c 100644 --- a/libsrc/protoserver/CMakeLists.txt +++ b/libsrc/protoserver/CMakeLists.txt @@ -1,37 +1,22 @@ - -# Define the current source locations -set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/protoserver) -set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/protoserver) - -include_directories( - ${CMAKE_CURRENT_BINARY_DIR} - ${PROTOBUF_INCLUDE_DIRS} -) - -set(ProtoServer_PROTOS ${CURRENT_SOURCE_DIR}/message.proto ) - -protobuf_generate_cpp(ProtoServer_PROTO_SRCS ProtoServer_PROTO_HDRS ${ProtoServer_PROTOS} ) -### Split protoclient from protoserver as protoserver relates to HyperionDaemon and standalone capture binarys can't link to it - -add_library(protoclient - ${CURRENT_SOURCE_DIR}/ProtoClientConnection.h - ${CURRENT_SOURCE_DIR}/ProtoClientConnection.cpp - ${ProtoServer_PROTO_SRCS} - ${ProtoServer_PROTO_HDRS} -) - -add_library(protoserver - ${CURRENT_HEADER_DIR}/ProtoServer.h - ${CURRENT_SOURCE_DIR}/ProtoServer.cpp -) +# set and compile proto schema +set(ProtoServer_PROTOS ${CMAKE_SOURCE_DIR}/libsrc/protoserver/message.proto) +protobuf_generate_cpp(ProtoServer_PROTO_SRCS ProtoServer_PROTO_HDRS ${ProtoServer_PROTOS}) # disable warnings for auto generated proto files, we can't change the files .... -if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) +if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) set_source_files_properties(${ProtoServer_PROTO_SRCS} ${ProtoServer_PROTO_HDRS} ${ProtoServer_PROTOS} PROPERTIES COMPILE_FLAGS "-w -Wno-return-local-addr") elseif(MSVC) set_source_files_properties(${ProtoServer_PROTO_SRCS} ${ProtoServer_PROTO_HDRS} ${ProtoServer_PROTOS} PROPERTIES COMPILE_FLAGS "/W0") endif() +### Split protoclient from protoserver as protoserver relates to HyperionDaemon and standalone capture binarys can't link to it +add_library(protoclient + ${CMAKE_SOURCE_DIR}/libsrc/protoserver/ProtoClientConnection.h + ${CMAKE_SOURCE_DIR}/libsrc/protoserver/ProtoClientConnection.cpp + ${ProtoServer_PROTO_SRCS} + ${ProtoServer_PROTO_HDRS} +) + target_link_libraries(protoclient hyperion hyperion-utils @@ -39,6 +24,16 @@ target_link_libraries(protoclient Qt${QT_VERSION_MAJOR}::Gui ) +target_include_directories(protoclient PUBLIC + ${CMAKE_CURRENT_BINARY_DIR} + ${PROTOBUF_INCLUDE_DIRS} +) + +add_library(protoserver + ${CMAKE_SOURCE_DIR}/include/protoserver/ProtoServer.h + ${CMAKE_SOURCE_DIR}/libsrc/protoserver/ProtoServer.cpp +) + target_link_libraries(protoserver hyperion hyperion-utils diff --git a/libsrc/python/CMakeLists.txt b/libsrc/python/CMakeLists.txt index 6b83eb14..d39d34af 100644 --- a/libsrc/python/CMakeLists.txt +++ b/libsrc/python/CMakeLists.txt @@ -1,34 +1,30 @@ -# Include the python directory. Also include the parent (which is for example /usr/include) -# which may be required when it is not includes by the (cross-) compiler by default. -if (NOT CMAKE_VERSION VERSION_LESS "3.12") - find_package(Python3 COMPONENTS Interpreter Development REQUIRED) - include_directories(${Python3_INCLUDE_DIRS} ${Python3_INCLUDE_DIRS}/..) - add_compile_definitions(PYTHON_VERSION_MAJOR=${Python3_VERSION_MAJOR}) - add_compile_definitions(PYTHON_VERSION_MINOR=${Python3_VERSION_MINOR}) -else() - find_package (PythonLibs ${PYTHON_VERSION_STRING} EXACT) # Maps PythonLibs to the PythonInterp version of the main cmake - include_directories(${PYTHON_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}/..) - add_definitions(-DPYTHON_VERSION_MAJOR=${PYTHON_VERSION_MAJOR}) - add_definitions(-DPYTHON_VERSION_MINOR=${PYTHON_VERSION_MINOR}) -endif() - -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/python) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/python) - -FILE ( GLOB PYTHON_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - add_library(python - ${PYTHON_SOURCES} + ${CMAKE_SOURCE_DIR}/include/python/PythonInit.h + ${CMAKE_SOURCE_DIR}/include/python/PythonProgram.h + ${CMAKE_SOURCE_DIR}/include/python/PythonUtils.h + ${CMAKE_SOURCE_DIR}/libsrc/python/PythonInit.cpp + ${CMAKE_SOURCE_DIR}/libsrc/python/PythonProgram.cpp ) +if(NOT CMAKE_VERSION VERSION_LESS "3.12") + find_package(Python3 COMPONENTS Interpreter Development REQUIRED) +else() + find_package (PythonLibs ${PYTHON_VERSION_STRING} EXACT) +endif() + target_link_libraries(python effectengine hyperion-utils + ${Python3_LIBRARIES} + ${PYTHON_LIBRARIES} ) -if (NOT CMAKE_VERSION VERSION_LESS "3.12") - target_link_libraries( python ${Python3_LIBRARIES} ) -else() - target_link_libraries( python ${PYTHON_LIBRARIES} ) -endif() +target_include_directories(python PUBLIC + ${Python3_INCLUDE_DIRS} ${Python3_INCLUDE_DIRS}/.. + ${PYTHON_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}/.. +) + +target_compile_definitions(python PRIVATE + PYTHON_VERSION_MAJOR=$<$:${Python3_VERSION_MAJOR}>$<$:${PYTHON_VERSION_MAJOR}> + PYTHON_VERSION_MINOR=$<$:${Python3_VERSION_MINOR}>$<$:${PYTHON_VERSION_MINOR}> +) diff --git a/libsrc/ssdp/CMakeLists.txt b/libsrc/ssdp/CMakeLists.txt index 2c007c0c..b773b6f9 100644 --- a/libsrc/ssdp/CMakeLists.txt +++ b/libsrc/ssdp/CMakeLists.txt @@ -1,14 +1,13 @@ -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/ssdp) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/ssdp) - -FILE ( GLOB SSDP_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - add_library(ssdp - ${SSDP_SOURCES} + ${CMAKE_SOURCE_DIR}/include/ssdp/SSDPDiscover.h + ${CMAKE_SOURCE_DIR}/include/ssdp/SSDPHandler.h + ${CMAKE_SOURCE_DIR}/include/ssdp/SSDPServer.h + ${CMAKE_SOURCE_DIR}/libsrc/ssdp/SSDPDescription.h + ${CMAKE_SOURCE_DIR}/libsrc/ssdp/SSDPDiscover.cpp + ${CMAKE_SOURCE_DIR}/libsrc/ssdp/SSDPHandler.cpp + ${CMAKE_SOURCE_DIR}/libsrc/ssdp/SSDPServer.cpp ) target_link_libraries(ssdp - Qt${QT_VERSION_MAJOR}::Network webserver ) diff --git a/libsrc/utils/CMakeLists.txt b/libsrc/utils/CMakeLists.txt index e51fcb5d..b7d28eeb 100644 --- a/libsrc/utils/CMakeLists.txt +++ b/libsrc/utils/CMakeLists.txt @@ -1,32 +1,94 @@ -# Define the current source locations - -if(ENABLE_EFFECTENGINE) - # Include the python directory. Also include the parent (which is for example /usr/include) - # which may be required when it is not includes by the (cross-) compiler by default. - if (NOT CMAKE_VERSION VERSION_LESS "3.12") - find_package(Python3 COMPONENTS Interpreter Development REQUIRED) - include_directories(${Python3_INCLUDE_DIRS} ${Python3_INCLUDE_DIRS}/..) - add_compile_definitions(PYTHON_VERSION_MAJOR_MINOR=${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR}) - else() - find_package (PythonLibs ${PYTHON_VERSION_STRING} EXACT) # Maps PythonLibs to the PythonInterp version of the main cmake - include_directories(${PYTHON_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}/..) - add_definitions(-DPYTHON_VERSION_MAJOR_MINOR=${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}) - endif() -endif() - -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/utils) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/utils) - -FILE ( GLOB_RECURSE Utils_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) - -list(APPEND Utils_SOURCES "${CMAKE_SOURCE_DIR}/dependencies/include/oklab/ok_color.h") - -if ( NOT ENABLE_PROFILER ) - LIST ( REMOVE_ITEM Utils_SOURCES ${CURRENT_HEADER_DIR}/Profiler.h ${CURRENT_SOURCE_DIR}/Profiler.cpp ) +if(ENABLE_PROFILER) + set(PROFILER ${CURRENT_HEADER_DIR}/Profiler.h ${CURRENT_SOURCE_DIR}/Profiler.cpp) endif() add_library(hyperion-utils - ${Utils_SOURCES} + # Global defines/signal sharing + ${CMAKE_SOURCE_DIR}/include/utils/global_defines.h + ${CMAKE_SOURCE_DIR}/include/utils/GlobalSignals.h + # JSON Schema Checker + ${CMAKE_SOURCE_DIR}/include/utils/jsonschema/QJsonFactory.h + ${CMAKE_SOURCE_DIR}/include/utils/jsonschema/QJsonUtils.h + ${CMAKE_SOURCE_DIR}/include/utils/jsonschema/QJsonSchemaChecker.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/jsonschema/QJsonSchemaChecker.cpp + # Color ARGB/BGR/RGB/RGBA/RGBW etc. structures + ${CMAKE_SOURCE_DIR}/include/utils/ColorArgb.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/ColorArgb.cpp + ${CMAKE_SOURCE_DIR}/include/utils/ColorBgr.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/ColorBgr.cpp + ${CMAKE_SOURCE_DIR}/include/utils/ColorRgb.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/ColorRgb.cpp + ${CMAKE_SOURCE_DIR}/include/utils/ColorRgba.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/ColorRgba.cpp + ${CMAKE_SOURCE_DIR}/include/utils/ColorRgbScalar.h + ${CMAKE_SOURCE_DIR}/include/utils/ColorRgbw.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/ColorRgbw.cpp + # Image declaration + ${CMAKE_SOURCE_DIR}/include/utils/Image.h + ${CMAKE_SOURCE_DIR}/include/utils/ImageData.h + # Image resampler + ${CMAKE_SOURCE_DIR}/include/utils/ImageResampler.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/ImageResampler.cpp + # Color transformation (saturation/luminance) of RGB colors + ${CMAKE_SOURCE_DIR}/include/utils/ColorSys.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/ColorSys.cpp + # Color transformation (saturation/value) of Okhsv colors + ${CMAKE_SOURCE_DIR}/include/utils/OkhsvTransform.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/OkhsvTransform.cpp + # Signal handler + ${CMAKE_SOURCE_DIR}/include/utils/DefaultSignalHandler.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/DefaultSignalHandler.cpp + # File utilities + ${CMAKE_SOURCE_DIR}/include/utils/FileUtils.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/FileUtils.cpp + # JSON utilities + ${CMAKE_SOURCE_DIR}/include/utils/JsonUtils.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/JsonUtils.cpp + # Logger + ${CMAKE_SOURCE_DIR}/include/utils/Logger.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/Logger.cpp + # IP adress/Port checker + ${CMAKE_SOURCE_DIR}/include/utils/NetOrigin.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/NetOrigin.cpp + ${CMAKE_SOURCE_DIR}/include/utils/NetUtils.h + # Process namespace (Hyperion restart) + ${CMAKE_SOURCE_DIR}/include/utils/Process.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/Process.cpp + # Rgb single color adjustment/correction + ${CMAKE_SOURCE_DIR}/include/utils/RgbChannelAdjustment.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/RgbChannelAdjustment.cpp + # Color conversion/transformation + ${CMAKE_SOURCE_DIR}/include/utils/RgbToRgbw.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/RgbToRgbw.cpp + ${CMAKE_SOURCE_DIR}/include/utils/RgbTransform.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/RgbTransform.cpp + # System info class + ${CMAKE_SOURCE_DIR}/include/utils/SysInfo.h + ${CMAKE_SOURCE_DIR}/libsrc/utils/SysInfo.cpp + # Grabber pixel formats enumeration + ${CMAKE_SOURCE_DIR}/include/utils/PixelFormat.h + # Grabber playing modes enumeration + ${CMAKE_SOURCE_DIR}/include/utils/VideoMode.h + # Grabber video standards enumeration + ${CMAKE_SOURCE_DIR}/include/utils/VideoStandard.h + # SettingsManager utilities + ${CMAKE_SOURCE_DIR}/include/utils/settings.h + # Qt string utilities + ${CMAKE_SOURCE_DIR}/include/utils/QStringUtils.h + # QThread sleep class + ${CMAKE_SOURCE_DIR}/include/utils/Sleep.h + # Wait event loop function + ${CMAKE_SOURCE_DIR}/include/utils/WaitTime.h + # Weak connection + ${CMAKE_SOURCE_DIR}/include/utils/WeakConnect.h + # Semver namespace + ${CMAKE_SOURCE_DIR}/include/utils/version.hpp + # Utility methods for Hyperion class + ${CMAKE_SOURCE_DIR}/include/utils/hyperion.h + # Oklab color space + ${CMAKE_SOURCE_DIR}/dependencies/include/oklab/ok_color.h + # Performance tester + ${PROFILER} ) target_link_libraries(hyperion-utils diff --git a/libsrc/utils/Process.cpp b/libsrc/utils/Process.cpp index 9fdd7264..baa2723a 100644 --- a/libsrc/utils/Process.cpp +++ b/libsrc/utils/Process.cpp @@ -1,93 +1,92 @@ #ifdef _WIN32 -#include -#include -#include -#include -#include -namespace Process { + #include + #include + #include + #include + #include -void restartHyperion(int exitCode) -{ - Logger* log = Logger::getInstance("Process"); - Info(log, "Restarting hyperion ..."); + namespace Process + { + void restartHyperion(int exitCode) + { + Logger* log = Logger::getInstance("Process"); + Info(log, "Restarting hyperion ..."); - auto arguments = QCoreApplication::arguments(); - if (!arguments.contains("--wait-hyperion")) - arguments << "--wait-hyperion"; + auto arguments = QCoreApplication::arguments(); + if (!arguments.contains("--wait-hyperion")) + arguments << "--wait-hyperion"; - QProcess::startDetached(QCoreApplication::applicationFilePath(), arguments); + QProcess::startDetached(QCoreApplication::applicationFilePath(), arguments); - //Exit with non-zero code to ensure service deamon restarts hyperion - QCoreApplication::exit(exitCode); -} + //Exit with non-zero code to ensure service deamon restarts hyperion + QCoreApplication::exit(exitCode); + } -QByteArray command_exec(const QString& /*cmd*/, const QByteArray& /*data*/) -{ - return QSTRING_CSTR(QString()); -} -}; + QByteArray command_exec(const QString& /*cmd*/, const QByteArray& /*data*/) + { + return QSTRING_CSTR(QString()); + } + }; #else -#include -#include + #include + #include -#include -#include -#include + #include + #include + #include -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include -#include + #include -#include -#include + #include + #include -namespace Process { - - -void restartHyperion(int exitCode) -{ - Logger* log = Logger::getInstance("Process"); - Info(log, "Restarting hyperion ..."); - - std::cout << std::endl - << " *******************************************" << std::endl - << " * hyperion will restart now *" << std::endl - << " *******************************************" << std::endl << std::endl; - - auto arguments = QCoreApplication::arguments(); - if (!arguments.contains("--wait-hyperion")) - arguments << "--wait-hyperion"; - - QProcess::startDetached(QCoreApplication::applicationFilePath(), arguments); - - //Exit with non-zero code to ensure service deamon restarts hyperion - QCoreApplication::exit(exitCode); -} - -QByteArray command_exec(const QString& cmd, const QByteArray& /*data*/) -{ - char buffer[128]; - QString result; - - std::shared_ptr pipe(popen(cmd.toLocal8Bit().constData(), "r"), pclose); - if (pipe) + namespace Process { - while (!feof(pipe.get())) + void restartHyperion(int exitCode) { - if (fgets(buffer, 128, pipe.get()) != nullptr) - result += buffer; - } - } - return QSTRING_CSTR(result); -} + Logger* log = Logger::getInstance("Process"); + Info(log, "Restarting hyperion ..."); -}; + std::cout << std::endl + << " *******************************************" << std::endl + << " * hyperion will restart now *" << std::endl + << " *******************************************" << std::endl << std::endl; + + auto arguments = QCoreApplication::arguments(); + if (!arguments.contains("--wait-hyperion")) + arguments << "--wait-hyperion"; + + QProcess::startDetached(QCoreApplication::applicationFilePath(), arguments); + + //Exit with non-zero code to ensure service deamon restarts hyperion + QCoreApplication::exit(exitCode); + } + + QByteArray command_exec(const QString& cmd, const QByteArray& /*data*/) + { + char buffer[128]; + QString result; + + std::shared_ptr pipe(popen(cmd.toLocal8Bit().constData(), "r"), pclose); + if (pipe) + { + while (!feof(pipe.get())) + { + if (fgets(buffer, 128, pipe.get()) != nullptr) + result += buffer; + } + } + return QSTRING_CSTR(result); + } + }; #endif diff --git a/libsrc/webserver/CMakeLists.txt b/libsrc/webserver/CMakeLists.txt index 3dcfde15..12e5345f 100644 --- a/libsrc/webserver/CMakeLists.txt +++ b/libsrc/webserver/CMakeLists.txt @@ -1,29 +1,43 @@ -# Define the current source locations -set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/webserver) -set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/webserver) +file(GLOB_RECURSE webFiles RELATIVE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/assets/webconfig/*) +file(RELATIVE_PATH webConfigPath ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/assets/webconfig) -FILE ( GLOB WebConfig_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) -FILE ( GLOB_RECURSE webFiles RELATIVE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/assets/webconfig/* ) -FILE ( RELATIVE_PATH webConfigPath ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/assets/webconfig) +foreach(f ${webFiles}) + string(REPLACE "${webConfigPath}/" "" fname ${f}) + set(HYPERION_WEBCONFIG_RES "${HYPERION_WEBCONFIG_RES}\n\t\t${f}") +endforeach() -FOREACH( f ${webFiles} ) - STRING ( REPLACE "${webConfigPath}/" "" fname ${f}) - SET(HYPERION_WEBCONFIG_RES "${HYPERION_WEBCONFIG_RES}\n\t\t${f}") -ENDFOREACH() -CONFIGURE_FILE(${CURRENT_SOURCE_DIR}/WebConfig.qrc.in ${CMAKE_BINARY_DIR}/WebConfig.qrc ) -SET(WebConfig_RESOURCES ${CMAKE_BINARY_DIR}/WebConfig.qrc) +configure_file(${CMAKE_SOURCE_DIR}/libsrc/webserver/WebConfig.qrc.in ${CMAKE_BINARY_DIR}/WebConfig.qrc) add_library(webserver - ${WebConfig_SOURCES} - ${WebConfig_RESOURCES} + ${CMAKE_SOURCE_DIR}/include/webserver/WebServer.h + ${CMAKE_SOURCE_DIR}/libsrc/webserver/WebServer.cpp + ${CMAKE_SOURCE_DIR}/libsrc/webserver/CgiHandler.h + ${CMAKE_SOURCE_DIR}/libsrc/webserver/CgiHandler.cpp + ${CMAKE_SOURCE_DIR}/libsrc/webserver/QtHttpClientWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/webserver/QtHttpClientWrapper.cpp + ${CMAKE_SOURCE_DIR}/libsrc/webserver/QtHttpHeader.h + ${CMAKE_SOURCE_DIR}/libsrc/webserver/QtHttpHeader.cpp + ${CMAKE_SOURCE_DIR}/libsrc/webserver/QtHttpReply.h + ${CMAKE_SOURCE_DIR}/libsrc/webserver/QtHttpReply.cpp + ${CMAKE_SOURCE_DIR}/libsrc/webserver/QtHttpRequest.h + ${CMAKE_SOURCE_DIR}/libsrc/webserver/QtHttpRequest.cpp + ${CMAKE_SOURCE_DIR}/libsrc/webserver/QtHttpServer.h + ${CMAKE_SOURCE_DIR}/libsrc/webserver/QtHttpServer.cpp + ${CMAKE_SOURCE_DIR}/libsrc/webserver/StaticFileServing.h + ${CMAKE_SOURCE_DIR}/libsrc/webserver/StaticFileServing.cpp + ${CMAKE_SOURCE_DIR}/libsrc/webserver/WebJsonRpc.h + ${CMAKE_SOURCE_DIR}/libsrc/webserver/WebJsonRpc.cpp + ${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketClient.h + ${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketClient.cpp + ${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketUtils.h + ${CMAKE_BINARY_DIR}/WebConfig.qrc ) target_link_libraries(webserver hyperion hyperion-utils hyperion-api - Qt${QT_VERSION_MAJOR}::Network ) if(ENABLE_MDNS) diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index affe720b..1fba840c 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -2,20 +2,20 @@ # All files are available with their file name by calling ":/FILENAME". Probably you need to call Q_INIT_RESOURCE("resources") once # # Define the current source locations -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/resources) +set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/resources) # catch all files -FILE ( GLOB Hyperion_RESFILES "${CURRENT_SOURCE_DIR}/icons/*" "${CURRENT_SOURCE_DIR}/ssl/*" ) +file(GLOB Hyperion_RESFILES "${CURRENT_SOURCE_DIR}/icons/*" "${CURRENT_SOURCE_DIR}/ssl/*") # fill resources.qrc with RESFILES -FOREACH( f ${Hyperion_RESFILES} ) +foreach(f ${Hyperion_RESFILES}) get_filename_component(fname ${f} NAME) - SET(HYPERION_RES "${HYPERION_RES}\n\t\t${f}") -ENDFOREACH() + set(HYPERION_RES "${HYPERION_RES}\n\t\t${f}") +endforeach() # prep file -CONFIGURE_FILE(${CURRENT_SOURCE_DIR}/resources.qrc.in ${CMAKE_BINARY_DIR}/resources.qrc ) -SET(Hyperion_RES ${CMAKE_BINARY_DIR}/resources.qrc) +configure_file(${CURRENT_SOURCE_DIR}/resources.qrc.in ${CMAKE_BINARY_DIR}/resources.qrc) +set(Hyperion_RES ${CMAKE_BINARY_DIR}/resources.qrc) add_library(resources ${Hyperion_RES} diff --git a/resources/icons/hyperion-128px.png b/resources/icons/hyperion-128px.png new file mode 100644 index 0000000000000000000000000000000000000000..b208c95eaaec03ad60536d23c3312cb5164c9cc4 GIT binary patch literal 10834 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 literal 0 HcmV?d00001 diff --git a/resources/icons/hyperion-192px.png b/resources/icons/hyperion-192px.png new file mode 100644 index 0000000000000000000000000000000000000000..1aae38ac19e8f27f5d87005bcc16a9dc40f55d50 GIT binary patch literal 21900 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 literal 0 HcmV?d00001 diff --git a/resources/icons/hyperion-24px.png b/resources/icons/hyperion-24px.png new file mode 100644 index 0000000000000000000000000000000000000000..be90363c8f939945ff297ef74c29ffe4bc4288cf GIT binary patch literal 1894 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBDAAG{;hE;^%b*2hb1<+n z3NbJPS&Tr)z$nE4G7ZRL@M4sPvx68lplX;H7}_%#SfFa6fHVkr05M1pgl1mAh%j*h z6I`{#0%imoq)m`tVjYm;EbxddW?0+>{umUo3Q%e#RDspr3imfVamB1>jfNYSkzLEl1NlCV?QiN}Sf^&XRs)CuG zfu4bq9hZWFf=y9MnpKdC8&o@xXRDM^Qc_^0uU}qXu2*iXmtT~wZ)j<0sc&GUZ)Btk zRH0j3nOBlnp_^B%3^4>|j!SBBa#3bMNoIbY0?6FNr2NtnTO}osMQ{LdXKF@SFQHXy^S0x~O7b0S?bb5rw*fljkCG%zx; z0o#Kig>Wg7)(G5Mk)+VII_Kx)7X=q2Ca2mNLTy75LQ)NKxs`uWW-2gHK|u#njv)qB zZ=(+iSEO);L?T!e7>jmXHu~@sVaMgl@HiEiKX^P{978;KuZDQ%%eV^s+f`jvR$g}V z?v2;iXIU_@vmCN%Vd+~WbBIwu$m_(xK$RUzg*^KYeCv%?u_!zx=orBFupy=Jfj~=3 z&yk~ICye+$W+xwiUt5uuzU*g}S@wEWg>3@tUawZJd$sDqh@RxvgvV$HIvIY0lbSO51(^8P7`)^b&M@^vsuasmQka@D2_BcOG9nI@OsQ z|1ERS^LdkPvhI*!^@X&|tEP1#$9p#jc-sq~%rg9C`8m_V*jV`Pt}YLQ9ZPEU+uF`O z`uBI?jdzVq;pg`7^RGXA^z#LYHlb9G`>i*+60R}LIAwR}Dch{>-5ZRJLzD%;t*hVY+ zKuV*UcYb&VPm^26(z!=>zTW(NnN4I**gUz2tqejFwI=s>T)cSQARz96?lK8w-`F#? zPE&YU4>n0oxO`e5Y=_O$NmooN?oY4Y^Xccosoqod|5#s6K6t#3Rba)+yiax+v-S1` zI>o5l8%ic0dr?wn{(S0#-Y*A!tYS^5nxCNm$w^Igb0|Y^aLUBHQ9lirv^?^6Y5n^d1#kIHYk%qZZOHxj;+)vm{fwhwtF5N0gBy>8 zMco^P{5b-#Y0}G^q=L>WwKrawcQS0RMcr|Q?AuSOL>8>?yumN?QbOHf|H@N2x!kM1 z@v!%cT@%ppP55cJ@~-ynjIt%W`6R<^SvGENn`d#Qs+zaxu(_bogu7SSukOpq-^Tsn z=9ZA-qD+r&pM36Z;ncWJDPsAnEurbIiPA>5UL3D`vL^YiMe9Q^)*1J2<*Z4s`eMqq z|JtYRldC)uUldgdX5W?Ceppz1rBv5RxJ)>H-=cY1A`fbRmj0Rj`k+Vu0++Q+qQ9aB$zDo+yj+2_XQ^u9CU^r^{WSNqD?JFd!4 zIQziC;^DLPHUG^ddMCRV`|wDsztcHu`1jx5oAW=~cD;|_7uE*V9-gj#F6*2UngHUE B*Vh05 literal 0 HcmV?d00001 diff --git a/resources/icons/hyperion-256px.png b/resources/icons/hyperion-256px.png new file mode 100644 index 0000000000000000000000000000000000000000..43121db3f7c3ab2ad87d9405fbfbfd5a9a6a5a76 GIT binary patch literal 31694 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 N002ovPDHLkV1hAknq>e0 literal 0 HcmV?d00001 diff --git a/resources/icons/hyperion-36px.png b/resources/icons/hyperion-36px.png new file mode 100644 index 0000000000000000000000000000000000000000..6d50eff434c11a8d9d8c6e6f9e74a207c44a9df5 GIT binary patch literal 2462 zcmZ`*c|4SR7k@AqTd9yGOmhiWW{llj+-PVlV;u}3V+_VN!^|+0ELmDe_GFvlrq`QY zy|!GTY;8g*yGoWsl3nghyw&Hu?>*1w_nh-P=leb9{Lb(BE|$f?dH@bdfSrwH0cIRh-)&nC#a#?1 z03?tA&@RTArP+dJEjDLIb1^x7WMp!FN6Rs}e%NfM(M! zHyoj>s|#0EgR7~jun;QLus|A~p%O^_Whcph^Pq@SLNJL!Bas6k?7VmnGM%Omg|Z8M zmz_R|4APHEfz;h&u?`4lN8kvUD*XS^Xe6(Hps^!6v~TCyDW}IyhO{Ozi2hC}QUEcK z%4*F3p{k~#_pQ#~LO(kFiHiGY>QABnr5-2ylY{LjcmmM?v0LpY*H376=SZty5|K4j z_UPE~zxj6YdT{n!f12~oC4Td=Zqa~W5B~j&FyJ=^-VgzR2W5&f#Nzno9LyN^T zk~y@$ZZwP~H?4?nMsTkLE%(Q)xZiIm+E`8v;$`}beST{4$aTX#Sw=d^`@K%6xDG?u zAs(dd!Aqk?>HpWrdi<Gq@8ysgmn1JthamC0kUU_Gq}dPB4C3H zr*n@>w((Eih82IYPtPQJs#_RFP9%M0<74mE9R|)j@8Jf8?iVYLR{JtnX&-@qdB9z= z#j6kNb-UkW(b2asMQMC6WN5naORw^1UU!q81n9S_0dv^cLU8djYhjO#h+drtUnw^R z{@`SOq==cQWz~yHFka}>X3Fqzb5F^~6Q>+EBM=c7{jizraq2sj_gQo03h-BxyjSGC zZ;~vuv~*M}maWp{%G10d)5`J7l$!9dDFKZUN!FCEjE9)Z|M8;!mm-D|cKqxp+@7?a zji1s+hBPLgOK_M+?1Koj*A-iqAJq-h=(u?ChW7BPgWYZ3K_zPBZ8hV)rF#o~pebpG zFXszCdHdqVc&iC*gzj&M1+15|PCZGQJ&Yl7zDohe}F;Z4*w-S?t zHhqbZEn#Zro}32s@VN`Ovhz|jQ|3oFc}3cji#=u?0#uB_LDf-=&qlG2RHgvabXh(- zGIVAz*2;?7?`hXIn`YQ{%WWyOu1>0_?ap#-L#oLM@lge)irY;8RXRiYJ}82Sr{K|3cQm7EgJY%!9v+3NzK61DW$O#0 zQ9dqDB|$Zd;=O%y@^+0BF_4^;LkA-N_wb{^XAjTYg1Vt=6GnI>aWLT%QdCO zKMF?aGR28`;3B0>teNH46LK~ar=5yRIVazxzc=t4{w?;vLbc!sGx_KqVNY(EXj!Is z3Cv;o!@m)y?R+kQVG|E|EFK&{P&Lzzd@=0Ota*D(v48gdY$mF$GdJ<7XxR-4_H9y6 zJ7#n8wL-~2r!W+9-q+D6q;taQ#OH*4vc1@RTV-hnLGZo&lNNEY_uLjM<~RCqUC@U? zoKv<#1SlP<5WXs z_nVKe8tEEUE)cNMn2TIcbQQ1V*}y6>-2E-DCpnLMTBbwe7ni!tXK#Ki4eYcf2$@Us zK2_ZlW{h{O%zl~EBClP~1X|Upy{V#Uwy^fM>ROS{hSbE&A1!>yuFiPol7M5b*u=j9 zx5gPyQTrT6Gpe5yKNwu4Srj7U_`*W}{6j0>u&eK+uOP(F6+}dyAp-W!*Ylkh8s!zs z`A#i2yP)3Z6gW1_(y^BtFRdNtB`rJ6R@FW<$t0+MC{FP(JK{4SBRPf7oAPkcdIj?r zsHhd55DeOUa(6<*Gqh6VE6>-k{>97(H&@^1ZAjw%?t_N!3dK|%Y+UQVa`s(MrRd9s z+*aFtneHw%xX2KPGicpenZZk;b(8AxDg{;PO$xQ`#@;e@lJkXCS%JfS=jRWi#a>0G zcr*|0%{m`_O;&VGB*gdzp*O=v-mgIG;RifHPDi-Rt9z?CwY{qQbVdQa^zlBzlHN+X zl4`O=nevN{J?-;BW!1l~Tug9F>csScw$sq535;7@bo+@jSsCuL;j_;*B3e6(m_wn% zs|B=vKG-922W2aWCBCZSa*fwN*J)zQUru)!)i|rT{tIi-Fx=$tTIx{PN>%R+@cPgC zYFP>$!$S_v9`l1W8K=F}uK?)7^zHr0R5Rq7^tvo*431uXu*^5$VbA{SFg3PBl^MCm F`~~nl0*wFw literal 0 HcmV?d00001 diff --git a/resources/icons/hyperion-48px.png b/resources/icons/hyperion-48px.png new file mode 100644 index 0000000000000000000000000000000000000000..851676be2552a87c88b68f0f0a679ed92723aa4a GIT binary patch literal 2556 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 Hu0mjf1MF1f literal 0 HcmV?d00001 diff --git a/resources/icons/hyperion-72px.png b/resources/icons/hyperion-72px.png new file mode 100644 index 0000000000000000000000000000000000000000..5cb47103e986d49a71c78a1f65ee14635e108df7 GIT binary patch literal 4451 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 F_&-$)U=9EP literal 0 HcmV?d00001 diff --git a/resources/icons/hyperion-icon-32px.png b/resources/icons/hyperion-icon-32px.png deleted file mode 100644 index ee60b83b46e4ddeec406fbbd4fc126b753455062..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1454 zcmV;f1yTBmP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$^7)eAyR9Fe+R!fUqMHK$3>fY|# z@9CN8jARCh0gZ?zzAzvpLD5CB$im0Qm5_xCLH~h^AOs0w+$e(JUUcC~a3dlKStYZI z5GBD8LS8f7b8mOwXI1&t&2*R;GlopYEOMH!t8Ue)I;YO73V7H_Y+!(m<~CTi;gsEQ z_CZVvs-!4bVh7t|L7SxXJ-mjQBsoxIEoe z_H)E$71HV`vMW~D>qNLL%}TD!V0c|;5zOcs3|X~@PN&Vz6o%+R4W`hKo6x$69_ldt z03NESz$d%UR*_F3pu&PbKya7RRo1{pM%px!x=(~Dihio{FhLe&A{nJ(Jj_ibg6T-t zbzeO$uB)b8@vyo&qk&zS#1j{R`TCTcPb;b;A~bA8I0Y({;1{`2h@tEN(p=&3T0l{u z@W3FUKCzKl&6Hgo`zB13i4s+WV_C$x$cBkc#+e)!(ylH?{lD5zHkJ|(=W^hj2h?Vj z#I8#we&(rfzTSs-kL=*doLjbn^ml)r_~Y0QADx`u(@W{SON@cup&a?^XOTYtk{!QH zsZ@$Csa|9gCu;5l>Hq2qE5Q|5u_q z8Do2|B_G}8#qyjTd#%%`(T};b0DHqr}+)r9^n<=5xNVS@% z&$M$X>P`XXl%{F?fb$cRRoHjRxNny(n<2N4RZ2M*I1))^s%mSUhLNgD1!gJCR*jJC z-&DZ)%n2?Rv)v4pdugr97-F^}4m!SVY^!7tK84N^#r`Zd6o|m%eFMNo=0Uo z{>?}M%ag{pFmBaHK?~?$TBNEgw6wy@SOi&!5Mg<%WZu57?9jhmMx=r_LPxV|>OQ>E zN4Im|-;o;?yiMy*GsdLxh6sv8*nGuGXNdoUENqLiIvGund!cNkBbz1HP_Q(Y)y}1! zi~b2VtCZV^)>e}grvi-(ww*wW%R#t5QRpl@w6;;eIn(|8|IBko5jcKf*l?IkP)XviRhXbfnVF)iQ^GGQ-k z-MCDKrwPHDeT?mvkSn+FYL|?vU!id#bx^(6$-PP~EHcRDuD|n8dx|X163%qaCF{Y@ z8{b;PEG`;-l`oq|<|gjF$vau=TDO;ji?QU**q;4O`e#+Q`@4mIL3~+Toae2ykZHYe zF*`rg$F;dJM5jQl;qiTvqE#Ki_s8)3A$&!bsR3_mQHiHdpLub2pUyjQ=+vUFPlf35 zTA48#J!aG_uk)VG(bXBel@V&i2-|`IcFteHj_p^`R7<=Cm&DJoLD5Au%26F|LolEe ztx#DB;r%rFN?*qLDivhG3RH}$$RQOU3QVC-_FEesccal8NMScXj6fD!2+u+ zYI%X@_NC9hwO^Sz6tay}w2)N-B-|t6qbl`Y;pK#Wd0&I?z4g?3?`97sGh)Rzb+icF@+wYH`{^hd+UvSvH3GRx@ zybYScs|?1NC&DjeP1c2!`ofyza_{w8+>D;}gKK?Sh;L)&%D>D_ zh9+|^$nHdLjuBrz^X|m+;KnzyfYa)~=I>VXNrk;z{)dR~Z-TE>*Wdfr-2eap07*qo IM6N<$f}eP^umAu6 diff --git a/resources/icons/hyperion-icon-512px.png b/resources/icons/hyperion-icon-512px.png deleted file mode 100644 index 53ea8fe8feb1093db2049c3f8a8fe5acb24e27e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69597 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 -#include +#include #include class AmlogicWrapper : public QObject diff --git a/src/hyperion-aml/CMakeLists.txt b/src/hyperion-aml/CMakeLists.txt index cdea66fa..40b04df3 100644 --- a/src/hyperion-aml/CMakeLists.txt +++ b/src/hyperion-aml/CMakeLists.txt @@ -1,25 +1,10 @@ cmake_minimum_required(VERSION 3.5.0) project(hyperion-aml) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) - -include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver - ${FLATBUFFERS_INCLUDE_DIRS} -) - -set(Hyperion_AML_HEADERS - AmlogicWrapper.h -) - -set(Hyperion_AML_SOURCES - hyperion-aml.cpp - AmlogicWrapper.cpp -) - add_executable(${PROJECT_NAME} - ${Hyperion_AML_HEADERS} - ${Hyperion_AML_SOURCES} + AmlogicWrapper.h + AmlogicWrapper.cpp + hyperion-aml.cpp ) target_link_libraries(${PROJECT_NAME} @@ -29,27 +14,23 @@ target_link_libraries(${PROJECT_NAME} flatbuffers amlogic-grabber framebuffer-grabber - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets ) if(ENABLE_MDNS) - target_link_libraries(${PROJECT_NAME} mdns) + target_link_libraries(${PROJECT_NAME} mdns) else() - target_link_libraries(${PROJECT_NAME} ssdp) + target_link_libraries(${PROJECT_NAME} ssdp) endif() -if (ENABLE_AMLOGIC) - target_link_libraries(${PROJECT_NAME} - pcre16 dl z - ) +if(ENABLE_AMLOGIC) + target_link_libraries(${PROJECT_NAME} pcre16 dl z) endif() -install ( TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_aml" ) +install (TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_aml") if(CMAKE_HOST_UNIX) - install(CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\" )" COMPONENT "hyperion_aml" ) - install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_aml" ) - install(CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME} )" COMPONENT "hyperion_aml" ) + install(CODE "execute_process(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\")" COMPONENT "hyperion_aml") + install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_aml") + install(CODE "file (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME})" COMPONENT "hyperion_aml") endif(CMAKE_HOST_UNIX) diff --git a/src/hyperion-dispmanx/CMakeLists.txt b/src/hyperion-dispmanx/CMakeLists.txt index 07862bbd..c81d0fdb 100644 --- a/src/hyperion-dispmanx/CMakeLists.txt +++ b/src/hyperion-dispmanx/CMakeLists.txt @@ -1,48 +1,31 @@ cmake_minimum_required(VERSION 3.5.0) project(hyperion-dispmanx) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) - -include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver - ${FLATBUFFERS_INCLUDE_DIRS} -) - -set(Hyperion_Dispmanx_HEADERS +add_executable(${PROJECT_NAME} DispmanxWrapper.h -) - -set(Hyperion_Dispmanx_SOURCES - hyperion-dispmanx.cpp DispmanxWrapper.cpp + hyperion-dispmanx.cpp ) -add_executable( ${PROJECT_NAME} - ${Hyperion_Dispmanx_HEADERS} - ${Hyperion_Dispmanx_SOURCES} -) - -target_link_libraries( ${PROJECT_NAME} +target_link_libraries(${PROJECT_NAME} commandline hyperion-utils flatbufconnect flatbuffers dispmanx-grabber - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets ) if(ENABLE_MDNS) - target_link_libraries(${PROJECT_NAME} mdns) + target_link_libraries(${PROJECT_NAME} mdns) else() - target_link_libraries(${PROJECT_NAME} ssdp) + target_link_libraries(${PROJECT_NAME} ssdp) endif() -install ( TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_dispmanx" ) +install (TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_dispmanx") if(CMAKE_HOST_UNIX) - install(CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\" )" COMPONENT "hyperion_dispmanx" ) - install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_dispmanx" ) - install(CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME} )" COMPONENT "hyperion_dispmanx" ) + install(CODE "execute_process(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\")" COMPONENT "hyperion_dispmanx") + install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_dispmanx") + install(CODE "file (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME})" COMPONENT "hyperion_dispmanx") endif(CMAKE_HOST_UNIX) diff --git a/src/hyperion-dispmanx/DispmanxWrapper.h b/src/hyperion-dispmanx/DispmanxWrapper.h index e02e2c5f..ddbb8a77 100644 --- a/src/hyperion-dispmanx/DispmanxWrapper.h +++ b/src/hyperion-dispmanx/DispmanxWrapper.h @@ -2,7 +2,7 @@ // QT includes #include -#include +#include #include class DispmanxWrapper : public QObject @@ -16,7 +16,7 @@ public: ); const Image & getScreenshot(); - + bool open(); /// diff --git a/src/hyperion-framebuffer/CMakeLists.txt b/src/hyperion-framebuffer/CMakeLists.txt index 14ea2ce9..a9470f5c 100644 --- a/src/hyperion-framebuffer/CMakeLists.txt +++ b/src/hyperion-framebuffer/CMakeLists.txt @@ -1,55 +1,35 @@ cmake_minimum_required(VERSION 3.5.0) project(hyperion-framebuffer) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) - -include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver - ${FLATBUFFERS_INCLUDE_DIRS} -) - -set(Hyperion_FB_HEADERS +add_executable(${PROJECT_NAME} FramebufferWrapper.h -) - -set(Hyperion_FB_SOURCES - hyperion-framebuffer.cpp FramebufferWrapper.cpp + hyperion-framebuffer.cpp ) -add_executable( ${PROJECT_NAME} - ${Hyperion_FB_HEADERS} - ${Hyperion_FB_SOURCES} -) - -target_link_libraries( ${PROJECT_NAME} +target_link_libraries(${PROJECT_NAME} commandline hyperion-utils flatbufconnect flatbuffers framebuffer-grabber - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Gui - Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets ) if(ENABLE_MDNS) - target_link_libraries(${PROJECT_NAME} mdns) + target_link_libraries(${PROJECT_NAME} mdns) else() - target_link_libraries(${PROJECT_NAME} ssdp) + target_link_libraries(${PROJECT_NAME} ssdp) endif() -if (ENABLE_AMLOGIC) - target_link_libraries( ${PROJECT_NAME} - pcre16 dl z - ) +if(ENABLE_AMLOGIC) + target_link_libraries(${PROJECT_NAME} pcre16 dl z) endif() -install ( TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_framebuffer" ) +install (TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_framebuffer") if(CMAKE_HOST_UNIX) - install(CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\" )" COMPONENT "hyperion_framebuffer" ) - install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_framebuffer" ) - install(CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME} )" COMPONENT "hyperion_framebuffer" ) + install(CODE "execute_process(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\")" COMPONENT "hyperion_framebuffer") + install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_framebuffer") + install(CODE "file (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME})" COMPONENT "hyperion_framebuffer") endif(CMAKE_HOST_UNIX) diff --git a/src/hyperion-framebuffer/FramebufferWrapper.h b/src/hyperion-framebuffer/FramebufferWrapper.h index d9bbdfb1..9f1b74f2 100644 --- a/src/hyperion-framebuffer/FramebufferWrapper.h +++ b/src/hyperion-framebuffer/FramebufferWrapper.h @@ -3,7 +3,7 @@ // QT includes #include -#include +#include #include class FramebufferWrapper : public QObject diff --git a/src/hyperion-osx/CMakeLists.txt b/src/hyperion-osx/CMakeLists.txt index efb15cd1..48a127b7 100644 --- a/src/hyperion-osx/CMakeLists.txt +++ b/src/hyperion-osx/CMakeLists.txt @@ -1,43 +1,25 @@ cmake_minimum_required(VERSION 3.5.0) project(hyperion-osx) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Widgets REQUIRED) - -include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver - ${FLATBUFFERS_INCLUDE_DIRS} -) - -set(Hyperion_OSX_HEADERS +add_executable(${PROJECT_NAME} OsxWrapper.h -) - -set(Hyperion_OSX_SOURCES - hyperion-osx.cpp OsxWrapper.cpp + hyperion-osx.cpp ) -add_executable( ${PROJECT_NAME} - ${Hyperion_OSX_HEADERS} - ${Hyperion_OSX_SOURCES} -) - -target_link_libraries( ${PROJECT_NAME} +target_link_libraries(${PROJECT_NAME} commandline hyperion-utils flatbufconnect flatbuffers osx-grabber - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Gui - Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets ) if(ENABLE_MDNS) - target_link_libraries(${PROJECT_NAME} mdns) + target_link_libraries(${PROJECT_NAME} mdns) else() - target_link_libraries(${PROJECT_NAME} ssdp) + target_link_libraries(${PROJECT_NAME} ssdp) endif() -install ( TARGETS ${PROJECT_NAME} DESTINATION "." COMPONENT "hyperion_osx" ) +install (TARGETS ${PROJECT_NAME} DESTINATION "." COMPONENT "hyperion_osx") diff --git a/src/hyperion-osx/OsxWrapper.h b/src/hyperion-osx/OsxWrapper.h index ef352bd0..335e6519 100644 --- a/src/hyperion-osx/OsxWrapper.h +++ b/src/hyperion-osx/OsxWrapper.h @@ -2,7 +2,7 @@ // QT includes #include -#include +#include #include class OsxWrapper : public QObject diff --git a/src/hyperion-qt/CMakeLists.txt b/src/hyperion-qt/CMakeLists.txt index f35b8534..fe87ef8b 100644 --- a/src/hyperion-qt/CMakeLists.txt +++ b/src/hyperion-qt/CMakeLists.txt @@ -1,32 +1,18 @@ cmake_minimum_required(VERSION 3.5.0) project(hyperion-qt) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Widgets REQUIRED) - -include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver - ${FLATBUFFERS_INCLUDE_DIRS} -) - -set(Hyperion_QT_HEADERS - QtWrapper.h -) - -set(Hyperion_QT_SOURCES - QtWrapper.cpp - hyperion-qt.cpp -) - -# generate windows .rc file for this binary -if (WIN32) - include(${CMAKE_SOURCE_DIR}/cmake/win/win_rc.cmake) - generate_win_rc_file(${PROJECT_NAME}) +if(WIN32) + # generate windows .rc file for this binary + string(REPLACE "/" "\\\\" WIN_RC_ICON_PATH ${CMAKE_SOURCE_DIR}/cmake/nsis/installer.ico) + configure_file(${CMAKE_SOURCE_DIR}/cmake/win/win.rc.in ${CMAKE_BINARY_DIR}/win.rc) + set(WIN_RC_FILE ${CMAKE_BINARY_DIR}/win.rc) endif() add_executable(${PROJECT_NAME} - ${Hyperion_QT_HEADERS} - ${Hyperion_QT_SOURCES} - ${${PROJECT_NAME}_WIN_RC_PATH} + QtWrapper.h + QtWrapper.cpp + hyperion-qt.cpp + ${WIN_RC_FILE} ) target_link_libraries(${PROJECT_NAME} @@ -34,28 +20,25 @@ target_link_libraries(${PROJECT_NAME} qt-grabber flatbufconnect flatbuffers - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Gui - Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets ) if(ENABLE_MDNS) - target_link_libraries(${PROJECT_NAME} mdns) + target_link_libraries(${PROJECT_NAME} mdns) else() - target_link_libraries(${PROJECT_NAME} ssdp) + target_link_libraries(${PROJECT_NAME} ssdp) endif() if(APPLE) - install ( TARGETS ${PROJECT_NAME} DESTINATION "." COMPONENT "hyperion_qt" ) + install (TARGETS ${PROJECT_NAME} DESTINATION "." COMPONENT "hyperion_qt") elseif(NOT WIN32) - install ( TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_qt" ) + install (TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_qt") else() - install ( TARGETS ${PROJECT_NAME} DESTINATION "bin" COMPONENT "hyperion_qt" ) + install (TARGETS ${PROJECT_NAME} DESTINATION "bin" COMPONENT "hyperion_qt") endif() if(CMAKE_HOST_UNIX AND NOT APPLE) - install(CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\" )" COMPONENT "hyperion_qt" ) - install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_qt" ) - install(CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME} )" COMPONENT "hyperion_qt" ) + install(CODE "execute_process(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\")" COMPONENT "hyperion_qt") + install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_qt") + install(CODE "file (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME})" COMPONENT "hyperion_qt") endif() diff --git a/src/hyperion-qt/QtWrapper.h b/src/hyperion-qt/QtWrapper.h index e4e689a3..979b27aa 100644 --- a/src/hyperion-qt/QtWrapper.h +++ b/src/hyperion-qt/QtWrapper.h @@ -4,7 +4,7 @@ #include // Hyperion-Qt includes -#include +#include #include class QtWrapper : public QObject diff --git a/src/hyperion-remote/CMakeLists.txt b/src/hyperion-remote/CMakeLists.txt index a2805cad..ef60a7dc 100644 --- a/src/hyperion-remote/CMakeLists.txt +++ b/src/hyperion-remote/CMakeLists.txt @@ -1,68 +1,50 @@ cmake_minimum_required(VERSION 3.5.0) project(hyperion-remote) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) - -# The following I do not understand completely... -# libQtCore.so uses some hardcoded library path inside which are incorrect after copying the file RPi file system -# Therefore, an extra path is needed on which to find the required libraries -IF ( EXISTS ${CMAKE_FIND_ROOT_PATH}/lib/arm-linux-gnueabihf ) - LINK_DIRECTORIES(${LINK_DIRECTORIES} ${CMAKE_FIND_ROOT_PATH}/lib/arm-linux-gnueabihf) -ENDIF() - -set(hyperion-remote_HEADERS - JsonConnection.h) - -set(hyperion-remote_SOURCES - hyperion-remote.cpp - JsonConnection.cpp) - -# generate windows .rc file for this binary -if (WIN32) - include(${CMAKE_SOURCE_DIR}/cmake/win/win_rc.cmake) - generate_win_rc_file(${PROJECT_NAME}) +if(WIN32) + # generate windows .rc file for this binary + string(REPLACE "/" "\\\\" WIN_RC_ICON_PATH ${CMAKE_SOURCE_DIR}/cmake/nsis/installer.ico) + configure_file(${CMAKE_SOURCE_DIR}/cmake/win/win.rc.in ${CMAKE_BINARY_DIR}/win.rc) + set(WIN_RC_FILE ${CMAKE_BINARY_DIR}/win.rc) endif() add_executable(${PROJECT_NAME} - ${hyperion-remote_HEADERS} - ${hyperion-remote_SOURCES} - ${${PROJECT_NAME}_WIN_RC_PATH} + JsonConnection.h + JsonConnection.cpp + hyperion-remote.cpp + ${WIN_RC_FILE} ) target_link_libraries(${PROJECT_NAME} commandline hyperion-utils - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets ) -if (ENABLE_AMLOGIC) - target_link_libraries(${PROJECT_NAME} - pcre16 dl z - ) +if(ENABLE_AMLOGIC) + target_link_libraries(${PROJECT_NAME} pcre16 dl z) endif() if(ENABLE_MDNS) - target_link_libraries(${PROJECT_NAME} mdns) + target_link_libraries(${PROJECT_NAME} mdns) else() - target_link_libraries(${PROJECT_NAME} ssdp) + target_link_libraries(${PROJECT_NAME} ssdp) endif() if(ENABLE_EFFECTENGINE) - target_link_libraries(${PROJECT_NAME} effectengine) + target_link_libraries(${PROJECT_NAME} effectengine) endif() if(APPLE) - install ( TARGETS ${PROJECT_NAME} DESTINATION "." COMPONENT "hyperion_remote" ) + install (TARGETS ${PROJECT_NAME} DESTINATION "." COMPONENT "hyperion_remote") elseif(NOT WIN32) - install ( TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_remote" ) + install (TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_remote") else() - install ( TARGETS ${PROJECT_NAME} DESTINATION "bin" COMPONENT "hyperion_remote" ) + install (TARGETS ${PROJECT_NAME} DESTINATION "bin" COMPONENT "hyperion_remote") endif() if(CMAKE_HOST_UNIX AND NOT APPLE) - install(CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\" )" COMPONENT "hyperion_remote" ) - install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_remote" ) - install(CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME} )" COMPONENT "hyperion_remote" ) + install(CODE "execute_process(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\")" COMPONENT "hyperion_remote") + install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_remote") + install(CODE "file (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME})" COMPONENT "hyperion_remote") endif() diff --git a/src/hyperion-v4l2/CMakeLists.txt b/src/hyperion-v4l2/CMakeLists.txt index 1404d2ad..37189cdc 100644 --- a/src/hyperion-v4l2/CMakeLists.txt +++ b/src/hyperion-v4l2/CMakeLists.txt @@ -1,25 +1,10 @@ cmake_minimum_required(VERSION 3.5.0) project(hyperion-v4l2) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) - -include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver - ${FLATBUFFERS_INCLUDE_DIRS} -) - -set(Hyperion_V4L2_HEADERS - ScreenshotHandler.h -) - -set(Hyperion_V4L2_SOURCES - hyperion-v4l2.cpp - ScreenshotHandler.cpp -) - add_executable(${PROJECT_NAME} - ${Hyperion_V4L2_HEADERS} - ${Hyperion_V4L2_SOURCES} + ScreenshotHandler.h + ScreenshotHandler.cpp + hyperion-v4l2.cpp ) target_link_libraries(${PROJECT_NAME} @@ -28,27 +13,23 @@ target_link_libraries(${PROJECT_NAME} hyperion-utils flatbufconnect flatbuffers - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets ) if(ENABLE_MDNS) - target_link_libraries(${PROJECT_NAME} mdns) + target_link_libraries(${PROJECT_NAME} mdns) else() - target_link_libraries(${PROJECT_NAME} ssdp) + target_link_libraries(${PROJECT_NAME} ssdp) endif() -if (ENABLE_AMLOGIC) - target_link_libraries(${PROJECT_NAME} - pcre16 dl z - ) +if(ENABLE_AMLOGIC) + target_link_libraries(${PROJECT_NAME} pcre16 dl z) endif() -install ( TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_v4l2" ) +install (TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_v4l2") if(CMAKE_HOST_UNIX) - install(CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\" )" COMPONENT "hyperion_v4l2" ) - install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_v4l2" ) - install(CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME} )" COMPONENT "hyperion_v4l2" ) + install(CODE "execute_process(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\")" COMPONENT "hyperion_v4l2") + install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_v4l2") + install(CODE "file (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME})" COMPONENT "hyperion_v4l2") endif(CMAKE_HOST_UNIX) diff --git a/src/hyperion-v4l2/hyperion-v4l2.cpp b/src/hyperion-v4l2/hyperion-v4l2.cpp index 73195177..daef9567 100644 --- a/src/hyperion-v4l2/hyperion-v4l2.cpp +++ b/src/hyperion-v4l2/hyperion-v4l2.cpp @@ -10,7 +10,7 @@ #include // grabber includes -#include "grabber/V4L2Grabber.h" +#include "grabber/video/v4l2/V4L2Grabber.h" // flatbuf includes #include @@ -255,7 +255,7 @@ int main(int argc, char** argv) SSDPDiscover discover; host = discover.getFirstService(searchType::STY_FLATBUFSERVER); #endif - + QHostAddress address; if (!NetUtils::resolveHostToAddress(log, host, address, port)) { diff --git a/src/hyperion-x11/CMakeLists.txt b/src/hyperion-x11/CMakeLists.txt index 88095ae3..20ca0f7a 100644 --- a/src/hyperion-x11/CMakeLists.txt +++ b/src/hyperion-x11/CMakeLists.txt @@ -1,32 +1,14 @@ cmake_minimum_required(VERSION 3.5.0) project(hyperion-x11) -find_package(X11 REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Widgets REQUIRED) - - -include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver - ${X11_INCLUDES} - ${FLATBUFFERS_INCLUDE_DIRS} -) - if(APPLE) include_directories("/opt/X11/include") endif(APPLE) -set(Hyperion_X11_HEADERS - X11Wrapper.h -) - -set(Hyperion_X11_SOURCES - hyperion-x11.cpp - X11Wrapper.cpp -) - add_executable(${PROJECT_NAME} - ${Hyperion_X11_HEADERS} - ${Hyperion_X11_SOURCES} + X11Wrapper.h + X11Wrapper.cpp + hyperion-x11.cpp ) target_link_libraries(${PROJECT_NAME} @@ -35,25 +17,19 @@ target_link_libraries(${PROJECT_NAME} flatbufconnect flatbuffers x11-grabber - ${X11_LIBRARIES} - ${X11_Xrandr_LIB} - ${X11_Xrender_LIB} - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Gui - Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets ) if(ENABLE_MDNS) - target_link_libraries(${PROJECT_NAME} mdns) + target_link_libraries(${PROJECT_NAME} mdns) else() - target_link_libraries(${PROJECT_NAME} ssdp) + target_link_libraries(${PROJECT_NAME} ssdp) endif() -install ( TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_x11" ) +install (TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_x11") if(CMAKE_HOST_UNIX) - install(CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\" )" COMPONENT "hyperion_x11" ) - install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_x11" ) - install(CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME} )" COMPONENT "hyperion_x11" ) + install(CODE "execute_process(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\")" COMPONENT "hyperion_x11") + install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_x11") + install(CODE "file (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME})" COMPONENT "hyperion_x11") endif(CMAKE_HOST_UNIX) diff --git a/src/hyperion-x11/X11Wrapper.h b/src/hyperion-x11/X11Wrapper.h index d0f9f1f2..13dbfdff 100644 --- a/src/hyperion-x11/X11Wrapper.h +++ b/src/hyperion-x11/X11Wrapper.h @@ -4,7 +4,7 @@ #include // Hyperion-X11 includes -#include +#include #include class X11Wrapper : public QObject diff --git a/src/hyperion-xcb/CMakeLists.txt b/src/hyperion-xcb/CMakeLists.txt index f378aef2..98b2ba32 100644 --- a/src/hyperion-xcb/CMakeLists.txt +++ b/src/hyperion-xcb/CMakeLists.txt @@ -1,48 +1,31 @@ cmake_minimum_required(VERSION 3.5.0) project(hyperion-xcb) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Widgets REQUIRED) - -include_directories( - ${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver - ${FLATBUFFERS_INCLUDE_DIRS} -) - -set(Hyperion_XCB_HEADERS +add_executable(${PROJECT_NAME} XcbWrapper.h -) - -set(Hyperion_XCB_SOURCES hyperion-xcb.cpp XcbWrapper.cpp ) -add_executable(${PROJECT_NAME} - ${Hyperion_XCB_HEADERS} - ${Hyperion_XCB_SOURCES} -) - target_link_libraries(${PROJECT_NAME} commandline hyperion-utils flatbufconnect flatbuffers xcb-grabber - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets ) if(ENABLE_MDNS) - target_link_libraries(${PROJECT_NAME} mdns) + target_link_libraries(${PROJECT_NAME} mdns) else() - target_link_libraries(${PROJECT_NAME} ssdp) + target_link_libraries(${PROJECT_NAME} ssdp) endif() install (TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_xcb") if(CMAKE_HOST_UNIX) - install(CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\" )" COMPONENT "hyperion_xcb" ) - install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_xcb" ) - install(CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME} )" COMPONENT "hyperion_xcb" ) + install(CODE "execute_process(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\")" COMPONENT "hyperion_xcb") + install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_xcb") + install(CODE "file (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME})" COMPONENT "hyperion_xcb") endif(CMAKE_HOST_UNIX) diff --git a/src/hyperion-xcb/XcbWrapper.h b/src/hyperion-xcb/XcbWrapper.h index d75aa63f..e3806ade 100644 --- a/src/hyperion-xcb/XcbWrapper.h +++ b/src/hyperion-xcb/XcbWrapper.h @@ -4,7 +4,7 @@ #include // Hyperion-Xcb includes -#include +#include #include //Utils includes diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index 35326c22..4d227d81 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -1,41 +1,32 @@ -if (APPLE) +if(APPLE) project(Hyperion) else() project(hyperiond) endif() -if(ENABLE_EFFECTENGINE) - if (NOT CMAKE_VERSION VERSION_LESS "3.12") - find_package(Python3 COMPONENTS Interpreter Development REQUIRED) - include_directories(${Python3_INCLUDE_DIRS} ${Python3_INCLUDE_DIRS}/..) - else() - find_package (PythonLibs ${PYTHON_VERSION_STRING} EXACT) # Maps PythonLibs to the PythonInterp version of the main cmake - include_directories(${PYTHON_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}/..) - endif() -endif () +##################################### +############ Preparation ############ +##################################### -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network Gui Widgets REQUIRED) +if(WIN32) + # generate windows .rc file for this binary + string(REPLACE "/" "\\\\" WIN_RC_ICON_PATH ${CMAKE_SOURCE_DIR}/cmake/nsis/installer.ico) + configure_file(${CMAKE_SOURCE_DIR}/cmake/win/win.rc.in ${CMAKE_BINARY_DIR}/win.rc) + set(WIN_RC_FILE ${CMAKE_BINARY_DIR}/win.rc) -# generate windows .rc file for this binary -if (WIN32) - include(${CMAKE_SOURCE_DIR}/cmake/win/win_rc.cmake) - generate_win_rc_file(${PROJECT_NAME}) -endif(WIN32) - -# include resource files for macos bundle (copy LICENSE file and correct line breaks) -if (APPLE) + # promote hyperiond as GUI app + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") +elseif(APPLE) + # include resource files for macos bundle (copy LICENSE file and correct line breaks) configure_file(${CMAKE_SOURCE_DIR}/LICENSE ${CMAKE_BINARY_DIR}/LICENSE COPYONLY) execute_process(COMMAND bash -c "perl -pi -e 's/\n/\r/g' ${CMAKE_BINARY_DIR}/LICENSE") - set(BUNDLE_RESOURCE_FILES ${CMAKE_SOURCE_DIR}/cmake/osxbundle/Hyperion.icns ${CMAKE_BINARY_DIR}/LICENSE) - set_source_files_properties(${BUNDLE_RESOURCE_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) -endif(APPLE) + set(MACOS_BUNDLE_RESOURCE_FILES ${CMAKE_SOURCE_DIR}/cmake/osxbundle/Hyperion.icns ${CMAKE_BINARY_DIR}/LICENSE) + set_source_files_properties(${MACOS_BUNDLE_RESOURCE_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) +endif() -if (UNIX) - find_package(Qt${QT_VERSION_MAJOR} COMPONENTS DBus QUIET ) - if (Qt${QT_VERSION_MAJOR}DBus_FOUND) - set(hyperiond_POWER_MNG_DBUS "Qt${QT_VERSION_MAJOR}::DBus") - endif() -endif(UNIX) +##################################### +########### General steps ########### +##################################### add_executable(${PROJECT_NAME} console.h @@ -45,19 +36,10 @@ add_executable(${PROJECT_NAME} systray.cpp SuspendHandler.cpp main.cpp - ${hyperiond_WIN_RC_PATH} - ${BUNDLE_RESOURCE_FILES} + ${WIN_RC_FILE} + ${MACOS_BUNDLE_RESOURCE_FILES} ) -if (UNIX AND NOT APPLE AND Qt${QT_VERSION_MAJOR}DBus_FOUND) - target_compile_definitions(${PROJECT_NAME} PUBLIC HYPERION_HAS_DBUS) -endif() - -# promote hyperiond as GUI app -if (WIN32) - target_link_options(${PROJECT_NAME} PUBLIC /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup) -endif(WIN32) - target_link_libraries(${PROJECT_NAME} commandline hyperion @@ -70,18 +52,16 @@ target_link_libraries(${PROJECT_NAME} Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets - ${hyperiond_POWER_MNG_DBUS} ) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS DBus QUIET) +if(Qt${QT_VERSION_MAJOR}DBus_FOUND) + target_link_libraries(${PROJECT_NAME} "Qt${QT_VERSION_MAJOR}::DBus") +endif() + if(ENABLE_EFFECTENGINE) target_link_libraries(${PROJECT_NAME} effectengine python) - - if (NOT CMAKE_VERSION VERSION_LESS "3.12") - target_link_libraries( ${PROJECT_NAME} ${Python3_LIBRARIES} ) - else() - target_link_libraries( ${PROJECT_NAME} ${PYTHON_LIBRARIES} ) - endif() -endif () +endif() if(ENABLE_FLATBUF_SERVER) target_link_libraries(${PROJECT_NAME} flatbufserver) @@ -91,71 +71,71 @@ if(ENABLE_PROTOBUF_SERVER) target_link_libraries(${PROJECT_NAME} protoserver) endif() -if (ENABLE_AMLOGIC) - target_link_libraries(${PROJECT_NAME} - #Qt${QT_VERSION_MAJOR}::Core - pcre16 dl z - ) +if(ENABLE_AMLOGIC) + target_link_libraries(${PROJECT_NAME} pcre16 dl z) endif(ENABLE_AMLOGIC) -if (ENABLE_DISPMANX) +if(ENABLE_DISPMANX) target_link_libraries(${PROJECT_NAME} dispmanx-grabber) endif (ENABLE_DISPMANX) -if (ENABLE_FB) +if(ENABLE_FB) target_link_libraries(${PROJECT_NAME} framebuffer-grabber) endif (ENABLE_FB) -if (ENABLE_OSX) +if(ENABLE_OSX) target_link_libraries(${PROJECT_NAME} osx-grabber) endif (ENABLE_OSX) -if (ENABLE_V4L2) +if(ENABLE_V4L2) target_link_libraries(${PROJECT_NAME} v4l2-grabber) -endif () +endif() -if (ENABLE_MF) +if(ENABLE_MF) target_link_libraries(${PROJECT_NAME} mf-grabber) endif (ENABLE_MF) -if (ENABLE_AUDIO) +if(ENABLE_AUDIO) target_link_libraries(hyperiond audio-grabber) endif() -if (ENABLE_AMLOGIC) +if(ENABLE_AMLOGIC) target_link_libraries(${PROJECT_NAME} amlogic-grabber) endif (ENABLE_AMLOGIC) -if (ENABLE_X11) +if(ENABLE_X11) if(APPLE) include_directories("/opt/X11/include") endif(APPLE) target_link_libraries(${PROJECT_NAME} x11-grabber) endif (ENABLE_X11) -if (ENABLE_XCB) +if(ENABLE_XCB) target_link_libraries(${PROJECT_NAME} xcb-grabber) endif (ENABLE_XCB) -if (ENABLE_QT) +if(ENABLE_QT) target_link_libraries(${PROJECT_NAME} qt-grabber) endif (ENABLE_QT) -if (ENABLE_DX) - include_directories(${DIRECTX9_INCLUDE_DIRS}) +if(ENABLE_DX) target_link_libraries(${PROJECT_NAME} directx-grabber) endif (ENABLE_DX) -if (ENABLE_CEC) - target_link_libraries(${PROJECT_NAME} cechandler) +if(ENABLE_CEC) + target_link_libraries(${PROJECT_NAME} cechandler) endif (ENABLE_CEC) -if (ENABLE_MDNS) +if(ENABLE_MDNS) target_link_libraries(${PROJECT_NAME} mdns) endif() -if (APPLE) - set_target_properties( ${PROJECT_NAME} PROPERTIES +##################################### +########### Install steps ########### +##################################### + +if(APPLE) + set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE TRUE MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/osxbundle/Info.plist.in MACOSX_BUNDLE_BUNDLE_NAME "Hyperion" @@ -168,69 +148,83 @@ if (APPLE) MACOSX_BUNDLE_LONG_VERSION_STRING ${HYPERION_VERSION} ) - install ( TARGETS ${PROJECT_NAME} DESTINATION . COMPONENT "Hyperion") + install (TARGETS ${PROJECT_NAME} DESTINATION . COMPONENT "Hyperion") elseif(NOT WIN32) - install ( TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "Hyperion" ) - install ( DIRECTORY ${CMAKE_SOURCE_DIR}/bin/service DESTINATION "share/hyperion/" COMPONENT "Hyperion" ) - install ( FILES ${CMAKE_SOURCE_DIR}/effects/readme.txt DESTINATION "share/hyperion/effects" COMPONENT "Hyperion" ) - install ( FILES ${CMAKE_SOURCE_DIR}/resources/icons/hyperion-icon-32px.png DESTINATION "share/hyperion/icons" COMPONENT "Hyperion" ) + # install Hyperion/service files/effect folder + install (TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "Hyperion") + install (DIRECTORY ${CMAKE_SOURCE_DIR}/bin/service DESTINATION "share/hyperion" COMPONENT "Hyperion") + install (FILES ${CMAKE_SOURCE_DIR}/effects/readme.txt DESTINATION "share/hyperion/effects" COMPONENT "Hyperion") - # Desktop file for Hyperion - install ( FILES ${CMAKE_SOURCE_DIR}/cmake/desktop/hyperiond_128.png DESTINATION "share/hyperion/desktop" COMPONENT "Hyperion" ) - install ( FILES ${CMAKE_SOURCE_DIR}/cmake/desktop/hyperiond.desktop DESTINATION "share/hyperion/desktop" COMPONENT "Hyperion" ) + # install Hyperion icons + set(ICON_SIZES 16 22 24 32 36 48 64 72 96 128 192 256 512) + foreach(size ${ICON_SIZES}) + set(ICONS_FROM "${CMAKE_SOURCE_DIR}/resources/icons/hyperion-${size}px.png") + set(ICONS_TO "share/hyperion/icons/${size}x${size}/apps/") + install(FILES ${ICONS_FROM} DESTINATION ${ICONS_TO} RENAME "hyperion.png" COMPONENT "Hyperion") + endforeach(size) + + # install desktop/appstream file + install (FILES ${CMAKE_SOURCE_DIR}/cmake/desktop/hyperion.metainfo.xml DESTINATION "share/hyperion/desktop" COMPONENT "Hyperion") + install (FILES ${CMAKE_SOURCE_DIR}/cmake/desktop/hyperion.desktop DESTINATION "share/hyperion/desktop" COMPONENT "Hyperion") else() - install ( TARGETS ${PROJECT_NAME} DESTINATION "bin" COMPONENT "Hyperion" ) - install ( FILES ${CMAKE_SOURCE_DIR}/effects/readme.txt DESTINATION "effects" COMPONENT "Hyperion" ) - - #set( CMAKE_INSTALL_UCRT_LIBRARIES TRUE ) - #set( CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE ) - #include( InstallRequiredSystemLibraries ) + install (TARGETS ${PROJECT_NAME} DESTINATION "bin" COMPONENT "Hyperion") + install (FILES ${CMAKE_SOURCE_DIR}/effects/readme.txt DESTINATION "effects" COMPONENT "Hyperion") endif() -if (CMAKE_HOST_UNIX AND NOT APPLE) - install( CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\" )" COMPONENT "Hyperion" ) - install( FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME ${PROJECT_NAME} COMPONENT "Hyperion" ) - install( CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME})" COMPONENT "Hyperion" ) - install( FILES ${CMAKE_SOURCE_DIR}/bin/scripts/updateHyperionUser.sh DESTINATION "share/hyperion/scripts" COMPONENT "Hyperion" ) +if(CMAKE_HOST_UNIX AND NOT APPLE) + install(CODE "execute_process(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\")" COMPONENT "Hyperion") + install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME ${PROJECT_NAME} COMPONENT "Hyperion") + install(CODE "file (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME})" COMPONENT "Hyperion") + install(FILES ${CMAKE_SOURCE_DIR}/bin/scripts/updateHyperionUser.sh DESTINATION "share/hyperion/scripts" COMPONENT "Hyperion") endif() +###################################### +########## Additional steps ########## +###################################### + # Deploy Qt DLLs into the binary folder. # This is necessary for starting the application from within the IDE -if (WIN32) +if(WIN32) get_target_property(QT_QMAKE_EXECUTABLE Qt${QT_VERSION_MAJOR}::qmake IMPORTED_LOCATION) get_filename_component(QT_BIN_DIR "${QT_QMAKE_EXECUTABLE}" DIRECTORY) find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${QT_BIN_DIR}") - if (NOT WINDEPLOYQT_EXECUTABLE) + if(NOT WINDEPLOYQT_EXECUTABLE) find_program(WINDEPLOYQT_EXECUTABLE windeployqt) endif() - if (WINDEPLOYQT_EXECUTABLE AND NOT CMAKE_GITHUB_ACTION) + if(WINDEPLOYQT_EXECUTABLE AND NOT CMAKE_GITHUB_ACTION) set(WINDEPLOYQT_PARAMS_RUNTIME --verbose 0 --no-compiler-runtime --no-opengl-sw --no-system-d3d-compiler) message(STATUS "Found windeployqt: ${WINDEPLOYQT_EXECUTABLE} PATH_HINT:${QT_BIN_DIR}") add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${WINDEPLOYQT_EXECUTABLE} ${WINDEPLOYQT_PARAMS_RUNTIME} "$") endif() find_package(OpenSSL REQUIRED) - if (OPENSSL_FOUND) + if(OPENSSL_FOUND) string(REGEX MATCHALL "[0-9]+" openssl_versions "${OPENSSL_VERSION}") list(GET openssl_versions 0 openssl_version_major) list(GET openssl_versions 1 openssl_version_minor) - set(library_suffix "-${openssl_version_major}_${openssl_version_minor}") - if (CMAKE_SIZEOF_VOID_P EQUAL 8) - string(APPEND library_suffix "-x64") + set(open_ssl_version_suffix) + if(openssl_version_major VERSION_EQUAL 1 AND openssl_version_minor VERSION_EQUAL 1) + set(open_ssl_version_suffix "-1_1") + else() + set(open_ssl_version_suffix "-3") + endif() + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + string(APPEND open_ssl_version_suffix "-x64") endif() find_file(OPENSSL_SSL - NAMES "libssl${library_suffix}.dll" + NAMES "libssl${open_ssl_version_suffix}.dll" PATHS ${OPENSSL_INCLUDE_DIR}/.. ${OPENSSL_INCLUDE_DIR}/../bin NO_DEFAULT_PATH ) find_file(OPENSSL_CRYPTO - NAMES "libcrypto${library_suffix}.dll" + NAMES "libcrypto${open_ssl_version_suffix}.dll" PATHS ${OPENSSL_INCLUDE_DIR}/.. ${OPENSSL_INCLUDE_DIR}/../bin NO_DEFAULT_PATH ) @@ -248,11 +242,11 @@ if(ENABLE_DEPLOY_DEPENDENCIES) # Deploy all dependencies for package creation include(${CMAKE_SOURCE_DIR}/cmake/Dependencies.cmake) - if (APPLE) #macOS + if(APPLE) #macOS DeployMacOS(${PROJECT_NAME}) elseif (NOT WIN32) # Linux DeployLinux(${PROJECT_NAME}) elseif(WIN32) # Windows DeployWindows(${PROJECT_NAME}) - endif () + endif() endif(ENABLE_DEPLOY_DEPENDENCIES) diff --git a/src/hyperiond/hyperiond.h b/src/hyperiond/hyperiond.h index 2a292746..c8b63113 100644 --- a/src/hyperiond/hyperiond.h +++ b/src/hyperiond/hyperiond.h @@ -7,62 +7,62 @@ #include #ifdef ENABLE_DISPMANX - #include + #include #else typedef QObject DispmanxWrapper; #endif #if defined(ENABLE_V4L2) || defined(ENABLE_MF) - #include + #include #else typedef QObject VideoWrapper; #endif #ifdef ENABLE_FB - #include + #include #else typedef QObject FramebufferWrapper; #endif #ifdef ENABLE_AMLOGIC - #include + #include #else typedef QObject AmlogicWrapper; #endif #ifdef ENABLE_OSX - #include + #include #else typedef QObject OsxWrapper; #endif #ifdef ENABLE_X11 - #include + #include #else typedef QObject X11Wrapper; #endif #ifdef ENABLE_XCB - #include + #include #else typedef QObject XcbWrapper; #endif #ifdef ENABLE_QT - #include + #include #else typedef QObject QtWrapper; #endif #ifdef ENABLE_DX - #include + #include #else typedef QObject DirectXWrapper; #endif #include #ifdef ENABLE_AUDIO - #include + #include #else typedef QObject AudioWrapper; #endif @@ -211,8 +211,8 @@ private: OsxWrapper* _osxGrabber; QtWrapper* _qtGrabber; DirectXWrapper* _dxGrabber; - AudioWrapper* _audioGrabber; SSDPHandler* _ssdp; + AudioWrapper* _audioGrabber; #ifdef ENABLE_CEC CECHandler* _cecHandler; #endif diff --git a/src/hyperiond/main.cpp b/src/hyperiond/main.cpp index fa9352b9..9bdda136 100644 --- a/src/hyperiond/main.cpp +++ b/src/hyperiond/main.cpp @@ -115,7 +115,7 @@ QCoreApplication* createApplication(int &argc, char *argv[]) app->addLibraryPath(QApplication::applicationDirPath() + "/../lib"); app->setApplicationDisplayName("Hyperion"); #ifndef __APPLE__ - app->setWindowIcon(QIcon(":/hyperion-icon-32px.png")); + app->setWindowIcon(QIcon(":/hyperion-32px.png")); #endif return app; } diff --git a/src/hyperiond/systray.cpp b/src/hyperiond/systray.cpp index ab7b6c8a..34f8de0a 100644 --- a/src/hyperiond/systray.cpp +++ b/src/hyperiond/systray.cpp @@ -33,8 +33,8 @@ SysTray::SysTray(HyperionDaemon *hyperiond) , _hyperiond(hyperiond) , _hyperion(nullptr) , _instanceManager(HyperionIManager::getInstance()) - , _suspendHandler (hyperiond->getSuspendHandlerInstance()) , _webPort(8090) + , _suspendHandler (hyperiond->getSuspendHandlerInstance()) { Q_INIT_RESOURCE(resources); @@ -281,7 +281,7 @@ void SysTray::handleInstanceStateChange(InstanceState state, quint8 instance, co connect(quitAction, &QAction::triggered, _trayIcon, &QSystemTrayIcon::hide, Qt::DirectConnection); connect(&_colorDlg, &QColorDialog::currentColorChanged, this, &SysTray::setColor); - QIcon icon(":/hyperion-icon-32px.png"); + QIcon icon(":/hyperion-32px.png"); _trayIcon->setIcon(icon); _trayIcon->show(); setWindowIcon(icon); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0e447f57..28378df6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,17 +3,17 @@ include_directories(../libsrc) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED) -MACRO (link_to_hyperion TARGET) - target_link_libraries( ${TARGET} blackborder leddevice jsonserver hyperion-utils hyperion ) +macro (link_to_hyperion TARGET) + target_link_libraries(${TARGET} blackborder leddevice jsonserver hyperion-utils hyperion) if(ENABLE_EFFECTENGINE) - target_link_libraries( ${TARGET} effectengine ) + target_link_libraries(${TARGET} effectengine) endif() -ENDMACRO() +endmacro() if(ENABLE_DEV_SPI) # Add the simple test executable 'TestSpi' add_executable(test_spi TestSpi.cpp) - target_link_libraries( test_spi leddevice hyperion-utils hyperion ) + target_link_libraries(test_spi leddevice hyperion-utils hyperion) add_executable(spidev_test spidev_test.c) add_executable(gpio2spi switchPinCtrl.c) endif(ENABLE_DEV_SPI) @@ -42,12 +42,12 @@ endif(ENABLE_X11) add_executable(test_versions TestVersions.cpp) target_link_libraries(test_versions Qt${QT_VERSION_MAJOR}::Core) -add_executable(test_image2ledsmap TestImage2LedsMap.cpp "${CMAKE_BINARY_DIR}/resources.qrc" ) +add_executable(test_image2ledsmap TestImage2LedsMap.cpp "${CMAKE_BINARY_DIR}/resources.qrc") link_to_hyperion(test_image2ledsmap) ######### These tests are broken. May they fix someone ########## -#if (ENABLE_DISPMANX) +#if(ENABLE_DISPMANX) # add_subdirectory(dispmanx2png) #endif (ENABLE_DISPMANX) diff --git a/test/dispmanx2png/CMakeLists.txt b/test/dispmanx2png/CMakeLists.txt index 7dfdd759..80037b9b 100644 --- a/test/dispmanx2png/CMakeLists.txt +++ b/test/dispmanx2png/CMakeLists.txt @@ -1,5 +1,5 @@ # this is only available on real pi -IF ( "${PLATFORM}" MATCHES rpi) +if("${PLATFORM}" MATCHES rpi) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Gui REQUIRED) @@ -16,4 +16,4 @@ IF ( "${PLATFORM}" MATCHES rpi) target_link_libraries(dispmanx2png dispmanx-grabber Qt${QT_VERSION_MAJOR::Gui) -ENDIF() +endif() diff --git a/test/dispmanx2png/dispmanx2png.cpp b/test/dispmanx2png/dispmanx2png.cpp index 2739f4fd..2c2a1748 100644 --- a/test/dispmanx2png/dispmanx2png.cpp +++ b/test/dispmanx2png/dispmanx2png.cpp @@ -7,7 +7,7 @@ #include // Dispmanx grabber includes -#include +#include static bool running = true; From cd1046ac1f8dc564343d14dcab29df16283dbae7 Mon Sep 17 00:00:00 2001 From: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com> Date: Sat, 18 Nov 2023 06:36:04 +0000 Subject: [PATCH 64/98] Cleanup & README correction --- Installation.md | 53 ------------------------------------- README.md | 10 +++---- dependencies/CMakeLists.txt | 3 --- 3 files changed, 5 insertions(+), 61 deletions(-) delete mode 100644 Installation.md diff --git a/Installation.md b/Installation.md deleted file mode 100644 index f55c1cdf..00000000 --- a/Installation.md +++ /dev/null @@ -1,53 +0,0 @@ - -# Installation -This page contains general installation steps for Hyperion. - -## Windows & macOS -For Windows and macOS is an installation file available on our [Release page](https://github.com/hyperion-project/hyperion.ng/releases). - -## Linux: -On the following operating systems, Hyperion can currently be installed/updated using the method listed below: -- Raspbian Buster/Raspberry Pi OS and later (armhf/arm64) -- Debian Buster(10) and later (armhf/arm64/x86_64) -- Ubuntu 20.04 and later (armhf/arm64/x86_64) - -*** - -### Install Hyperion: -1. Add necessary packages for the installation: -```bash -sudo apt-get update && sudo apt-get install wget gpg apt-transport-https lsb-release -``` - -2. Add Hyperion’s official GPG key: -```bash -wget -qO- https://apt.hyperion-project.org/hyperion.pub.key | sudo gpg --dearmor -o /usr/share/keyrings/hyperion.pub.gpg -``` - -3. Add Hyperion-Project to your APT sources: -```bash -echo "deb [signed-by=/usr/share/keyrings/hyperion.pub.gpg] https://apt.hyperion-project.org/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hyperion.list -``` - -4. Update your local package index and install Hyperion: -```bash -sudo apt-get update && sudo apt-get install hyperion -``` -*** - -### Update Hyperion: -```bash -sudo apt-get install hyperion -``` -*** - -### If you want to uninstall Hyperion, use the following commands: -1. Remove Hyperion: -```bash -sudo apt-get --purge autoremove hyperion -``` - -2. Remove the Hyperion-Project APT source from your system: -```bash -sudo rm /usr/share/keyrings/hyperion.pub.gpg /etc/apt/sources.list.d/hyperion.list -``` diff --git a/README.md b/README.md index 028922b1..3259a81d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Latest-Release](https://img.shields.io/github/v/release/hyperion-project/hyperion.ng?include_prereleases&label=Latest%20Release&logo=github&logoColor=white&color=0f83e7)](https://github.com/hyperion-project/hyperion.ng/releases) -[![GitHub Actions](https://github.com/hyperion-project/hyperion.ng/workflows/Hyperion%20CI%20Build/badge.svg?branch=master)](https://github.com/hyperion-project/hyperion.ng/actions) +[![GitHub Actions](https://github.com/hyperion-project/hyperion.ng/actions/workflows/push_pull.yml/badge.svg)](https://github.com/hyperion-project/hyperion.ng/actions) [![CodeQL Analysis](https://github.com/hyperion-project/hyperion.ng/actions/workflows/codeql.yml/badge.svg)](https://github.com/hyperion-project/hyperion.ng/actions/workflows/codeql.yml) [![Forum](https://img.shields.io/website/https/hyperion-project.org.svg?label=Forum&down_color=red&down_message=offline&up_color=4bc51d&up_message=online&logo=homeadvisor&logoColor=white)](https://www.hyperion-project.org) [![Documentation](https://img.shields.io/website/https/docs.hyperion-project.org.svg?label=Documentation&down_color=red&down_message=offline&up_color=4bc51d&up_message=online&logo=read-the-docs)](https://docs.hyperion-project.org) @@ -45,7 +45,7 @@ For an example, you can participate in the translation.
## Supported Platforms -Find here more details on [supported platforms and configuration sets](doc/development/SupportedPlatforms.md) +Find here more details on [supported platforms and configuration sets](doc/development/SupportedPlatforms.md). ## Documentation Covers these topics: @@ -57,16 +57,16 @@ Covers these topics: [![Visit Documentation](https://img.shields.io/website/https/docs.hyperion-project.org.svg?label=Documentation&down_color=red&down_message=offline&up_color=4bc51d&up_message=online&logo=read-the-docs)](https://docs.hyperion-project.org) ## Changelog -Released and unreleased changes at [CHANGELOG.md](CHANGELOG.md) +Released and unreleased changes at [CHANGELOG.md](CHANGELOG.md). ## Building See [CompileHowto.md](doc/development/CompileHowto.md). ## Installation -See [Documentation](https://docs.hyperion-project.org/en/user/Installation.html) or at [Installation.md](Installation.md). +See [Documentation](https://docs.hyperion-project.org/en/user/Installation.html) or on the [Release Repository](https://releases.hyperion-project.org). ## Download -Releases available from the [Hyperion release page](https://github.com/hyperion-project/hyperion.ng/releases) +Releases available from the [Hyperion release page](https://github.com/hyperion-project/hyperion.ng/releases). ## Privacy Policy See [PRIVACY.md](PRIVACY.md). diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 28ba5d04..2669448d 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -48,10 +48,7 @@ if (ENABLE_MDNS) BUILD_ALWAYS OFF DOWNLOAD_COMMAND "" SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/qmdnsengine - BINARY_DIR ${CMAKE_BINARY_DIR}/dependencies/external/qmdnsengine/build CMAKE_ARGS ${QMDNS_CMAKE_ARGS} - CONFIGURE_COMMAND ${CMAKE_COMMAND} -S -B ${QMDNS_CMAKE_ARGS} -G ${CMAKE_GENERATOR} - BUILD_COMMAND ${CMAKE_COMMAND} --build --config $ INSTALL_DIR ${CMAKE_BINARY_DIR} BUILD_BYPRODUCTS ${QMDNS_LIBRARIES} ) From 2e0cc9cfa807c0080c849120f85df758329a4cd3 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:51:49 +0100 Subject: [PATCH 65/98] Improvements (#1658) * Restart Servial Device, if write error occurred. * Fix typos * Fix FOUND_USER command * Use SUDO_USER in postinst * Align install_pr with new package names * Package named arm64 instead of aarch64 * Update Change log * Fix to have the architecture resolved * All tls plugin directory for Qt6 --------- Co-authored-by: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com> --- .github/workflows/qt5_6.yml | 16 +-- CHANGELOG.md | 1 + assets/webconfig/content/update.html | 118 +++++++++--------- bin/scripts/docker-compile.sh | 4 +- bin/scripts/install_pr.sh | 60 ++++++--- cmake/Dependencies.cmake | 4 +- cmake/package-scripts/postinst | 2 +- libsrc/leddevice/LedDevice.cpp | 2 +- .../leddevice/dev_net/LedDevicePhilipsHue.cpp | 2 +- libsrc/leddevice/dev_serial/ProviderRs232.cpp | 14 ++- 10 files changed, 129 insertions(+), 94 deletions(-) diff --git a/.github/workflows/qt5_6.yml b/.github/workflows/qt5_6.yml index ac4622b3..5b08ef41 100644 --- a/.github/workflows/qt5_6.yml +++ b/.github/workflows/qt5_6.yml @@ -41,13 +41,13 @@ jobs: fail-fast: false matrix: os: [ - { distribution: debian, codename: buster, description: Debian Buster (x86_64), architecture: [ amd64, linux/amd64 ] }, - { distribution: debian, codename: bullseye, description: Debian Bullseye (x86_64), architecture: [ amd64, linux/amd64 ] }, - { distribution: debian, codename: buster, description: Debian Buster (Raspberry Pi 1/ZERO), architecture: [ armv6, linux/arm/v5 ] }, - { distribution: debian, codename: buster, description: Debian Buster (Raspberry Pi 2/3/4), architecture: [ armv7, linux/arm/v7 ] }, - { distribution: debian, codename: bullseye, description: Debian Bullseye (Raspberry Pi 2/3/4), architecture: [ armv7, linux/arm/v7 ] }, - { distribution: debian, codename: buster, description: Debian Buster (Generic AARCH64), architecture: [ aarch64, linux/arm64 ] }, - { distribution: debian, codename: bullseye, description: Debian Bullseye (Generic AARCH64), architecture: [ aarch64, linux/arm64 ] } + { distribution: debian, codename: buster, description: Debian Buster (x86_64), architecture: [ amd64, linux/amd64 ] }, + { distribution: debian, codename: bullseye, description: Debian Bullseye (x86_64), architecture: [ amd64, linux/amd64 ] }, + { distribution: debian, codename: buster, description: Debian Buster (Raspberry Pi 1/ZERO), architecture: [ armv6, linux/arm/v5 ] }, + { distribution: debian, codename: buster, description: Debian Buster (Raspberry Pi 2/3/4), architecture: [ armv7, linux/arm/v7 ] }, + { distribution: debian, codename: bullseye, description: Debian Bullseye (Raspberry Pi 2/3/4), architecture: [ armv7, linux/arm/v7 ] }, + { distribution: debian, codename: buster, description: Debian Buster (Generic AARCH64), architecture: [ arm64, linux/arm64 ] }, + { distribution: debian, codename: bullseye, description: Debian Bullseye (Generic AARCH64), architecture: [ arm64, linux/arm64 ] } ] isQt5: - ${{ inputs.qt_version == '5' }} @@ -58,7 +58,7 @@ jobs: platform: rpi - os.architecture[0]: armv7 platform: rpi - - os.architecture[0]: aarch64 + - os.architecture[0]: arm64 platform: amlogic exclude: - isQt5: true diff --git a/CHANGELOG.md b/CHANGELOG.md index e4024253..bb8bbeb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Note: The wizard will configure an APIv2 capable bridge always with Entertainmen ### Changed - Updated misleading error messages in case Hyperion is not able to support the suspend/lock feature (#1622) +- Restart Serial Device, if write error occurred - ws281x - Update logic to identify is user is admin and disable device configuration if not (#1621) - Hide Hyperion from the Dock on macOS, as all features can be accessed from the menubar - Thanks @Rastafabisch diff --git a/assets/webconfig/content/update.html b/assets/webconfig/content/update.html index f4e8aa28..3a638bd7 100644 --- a/assets/webconfig/content/update.html +++ b/assets/webconfig/content/update.html @@ -1,68 +1,70 @@
-
-
- -
-

Overview about all available Hyperion versions. On top you could update or downgrade your version of Hyperion whenever you want. Sorted from newest to oldest

-

At the moment the respective install button is disabled. Development is still ongoing here.

-
-
-

-
-
-
-
-
+
+
+ +
+

Overview about all available Hyperion versions. On top you could update or downgrade your version of Hyperion whenever you want. Sorted from newest to oldest

+

At the moment the respective install button is disabled. Development is still ongoing here.

+
+
+

+
+
+
+
+
diff --git a/bin/scripts/docker-compile.sh b/bin/scripts/docker-compile.sh index bafde87f..82a098b7 100755 --- a/bin/scripts/docker-compile.sh +++ b/bin/scripts/docker-compile.sh @@ -126,7 +126,7 @@ do shift BUILD_PLATFORM=$1 ;; - --qt5) + --qt5) BUILD_WITH_QT5=true ;; -l|--local) @@ -214,7 +214,7 @@ if [ $ARCHITECTURE != $CURRENT_ARCHITECTURE ]; then ENTRYPOINT_OPTION= if [ $CURRENT_ARCHITECTURE != "amd64" ]; then - echo "---> Emulation builds can only be executed on linux/amd64, linux/x86_64 platforms, current architecture is ${CURRENT_ARCHITECTURE}" + echo "---> Emulation builds can only be executed on linux/amd64, linux/x86_64 platforms, current architecture is ${CURRENT_ARCHITECTURE}" exit 1 fi else diff --git a/bin/scripts/install_pr.sh b/bin/scripts/install_pr.sh index cd023504..0080d6d8 100755 --- a/bin/scripts/install_pr.sh +++ b/bin/scripts/install_pr.sh @@ -12,6 +12,11 @@ hasPython3=$? type python > /dev/null 2> /dev/null hasPython2=$? +DISTRIBUTION="debian" +CODENAME="bullseye" +ARCHITECTURE="" +WITH_QT5=false + BASE_PATH='.'; if [[ "${hasWget}" -ne 0 ]] && [[ "${hasCurl}" -ne 0 ]]; then @@ -38,19 +43,20 @@ function request_call() { fi } -while getopts ":c:t:m:r:" opt; do +while getopts ":a:c:r:t:5" opt; do case "$opt" in - t) PR_TOKEN=$OPTARG ;; - r) run_id=$OPTARG ;; - m) ARCHITECTURE=$OPTARG ;; + a) ARCHITECTURE=$OPTARG ;; c) CONFIGDIR=$OPTARG ;; + r) run_id=$OPTARG ;; + t) PR_TOKEN=$OPTARG ;; + 5) WITH_QT5=true ;; esac done shift $(( OPTIND - 1 )) # Check for a command line argument (PR number) if [ "$1" == "" ] || [ $# -gt 1 ] || [ -z ${PR_TOKEN} ]; then - echo "Usage: $0 -t -m -r -c " >&2 + echo "Usage: $0 -t -a -r -c " >&2 exit 1 else pr_number="$1" @@ -68,30 +74,39 @@ if [[ -z ${ARCHITECTURE} ]]; then fi #Test if multiarchitecture setup, i.e. user-space is 32bit -if [ ${ARCHITECTURE} == "aarch64" ]; then +if [[ "${ARCHITECTURE}" == "aarch64" || "${ARCHITECTURE}" == "arm64" ]]; then + ARCHITECTURE="arm64" USER_ARCHITECTURE=$ARCHITECTURE IS_V7L=`cat /proc/$$/maps |grep -m1 -c v7l` if [ $IS_V7L -ne 0 ]; then - USER_ARCHITECTURE="armv7l" + USER_ARCHITECTURE="armv7" else IS_V6L=`cat /proc/$$/maps |grep -m1 -c v6l` if [ $IS_V6L -ne 0 ]; then - USER_ARCHITECTURE="armv6l" + USER_ARCHITECTURE="armv6" fi fi if [ $ARCHITECTURE != $USER_ARCHITECTURE ]; then - echo "---> Identified kernel target architecture: $ARCHITECTURE" echo "---> Identified user space target architecture: $USER_ARCHITECTURE" ARCHITECTURE=$USER_ARCHITECTURE fi +else + ARCHITECTURE=${ARCHITECTURE//x86_/amd} fi -echo 'armv6l armv7l aarch64 x86_64' | grep -qw ${ARCHITECTURE} +echo 'armv6l armv7l arm64 amd64' | grep -qw ${ARCHITECTURE} if [ $? -ne 0 ]; then echo "---> Critical Error: Target architecture $ARCHITECTURE is unknown -> abort" exit 1 else - echo "---> Download Pull Request for identified runtime architecture: $ARCHITECTURE" + PACKAGE="${ARCHITECTURE}" + QTVERSION="5" + if [ ${WITH_QT5} == false ]; then + QTVERSION="6" + PACKAGE="${PACKAGE}_qt6" + fi + + echo "---> Download package for identified runtime architecture: $ARCHITECTURE and Qt$QTVERSION" fi # Determine if PR number exists @@ -153,14 +168,25 @@ fi # Get archive_download_url from workflow artifacts=$(request_call "$api_url/actions/runs/$run_id/artifacts") -archive_download_url=$(echo "$artifacts" | tr '\r\n' ' ' | ${pythonCmd} -c """ -import json,sys + +PACKAGE_NAME=$(echo "$artifacts" | tr '\r\n' ' ' | ${pythonCmd} -c """ +import json,sys, re data = json.load(sys.stdin) for i in data['artifacts']: - if i['name'] == '"$ARCHITECTURE"': - print(i['archive_download_url']) - break + if re.match('.*{}$'.format(re.escape('$PACKAGE')), i['name']): + print(i['name']) + break +""" 2>/dev/null) + +archive_download_url=$(echo "$artifacts" | tr '\r\n' ' ' | ${pythonCmd} -c """ +import json,sys, re +data = json.load(sys.stdin) + +for i in data['artifacts']: + if re.match('.*{}$'.format(re.escape('$PACKAGE')), i['name']): + print(i['archive_download_url']) + break """ 2>/dev/null) if [ -z "$archive_download_url" ]; then @@ -171,7 +197,7 @@ if [ -z "$archive_download_url" ]; then fi # Download packed PR artifact -echo "---> Downloading the Pull Request #$pr_number" +echo "---> Downloading Pull Request #$pr_number, package: $PACKAGE_NAME" if [ $hasCurl -eq 0 ]; then curl -skH "Authorization: token ${PR_TOKEN}" -o $BASE_PATH/temp.zip -L --get $archive_download_url elif [ $hasWget -eq 0 ]; then diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 847c4c40..4715bb61 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -46,7 +46,7 @@ macro(DeployMacOS TARGET) MESSAGE("The following unresolved dependencies were discovered: ${unresolved_deps}") endif() - foreach(PLUGIN "platforms" "sqldrivers" "imageformats") + foreach(PLUGIN "platforms" "sqldrivers" "imageformats" "tls") if(EXISTS ${PLUGIN_DIR}/${PLUGIN}) file(GLOB files "${PLUGIN_DIR}/${PLUGIN}/*") foreach(file ${files}) @@ -230,7 +230,7 @@ macro(DeployLinux TARGET) # Copy Qt plugins to 'share/hyperion/lib' if (QT_PLUGINS_DIR) - foreach(PLUGIN "platforms" "sqldrivers" "imageformats") + foreach(PLUGIN "platforms" "sqldrivers" "imageformats" "tls") if (EXISTS ${QT_PLUGINS_DIR}/${PLUGIN}) file(GLOB files "${QT_PLUGINS_DIR}/${PLUGIN}/*.so") foreach(file ${files}) diff --git a/cmake/package-scripts/postinst b/cmake/package-scripts/postinst index 54cd3901..88d2e12a 100644 --- a/cmake/package-scripts/postinst +++ b/cmake/package-scripts/postinst @@ -47,7 +47,7 @@ if [ "$IS_UPGRADE" = false ]; then then # systemd echo "---> init deamon: systemd" - FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" + FOUND_USR=${SUDO_USER:-root} install_file /usr/share/hyperion/service/hyperion.systemd /etc/systemd/system/hyperion@.service systemctl enable hyperion"@${FOUND_USR}".service START_MSG="--> systemctl start hyperion for user ${FOUND_USR}" diff --git a/libsrc/leddevice/LedDevice.cpp b/libsrc/leddevice/LedDevice.cpp index f49878c4..92ceb1e6 100644 --- a/libsrc/leddevice/LedDevice.cpp +++ b/libsrc/leddevice/LedDevice.cpp @@ -360,7 +360,7 @@ int LedDevice::rewriteLEDs() int LedDevice::writeBlack(int numberOfWrites) { - Debug(_log, "Set LED strip to black to switch of LEDs"); + Debug(_log, "Set LED strip to black to switch LEDs off"); return writeColor(ColorRgb::BLACK, numberOfWrites); } diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp index 77712594..ab6d0a17 100644 --- a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp @@ -1301,7 +1301,7 @@ QJsonObject LedDevicePhilipsHueBridge::discover(const QJsonObject& /*params*/) ); #else QString discoveryMethod("ssdp"); - deviceList = discover(); + deviceList = discoverSsdp(); #endif devicesDiscovered.insert("discoveryMethod", discoveryMethod); diff --git a/libsrc/leddevice/dev_serial/ProviderRs232.cpp b/libsrc/leddevice/dev_serial/ProviderRs232.cpp index ea4f99b9..aa0511fc 100644 --- a/libsrc/leddevice/dev_serial/ProviderRs232.cpp +++ b/libsrc/leddevice/dev_serial/ProviderRs232.cpp @@ -185,7 +185,7 @@ bool ProviderRs232::tryOpen(int delayAfterConnect_ms) } else { - QString errortext = QString("Invalid serial device name: %1 %2!").arg(_deviceName, _location); + QString errortext = QString("Invalid serial device: %1 %2!").arg(_deviceName, _location); this->setInError( errortext ); return false; } @@ -237,9 +237,9 @@ int ProviderRs232::writeBytes(const qint64 size, const uint8_t *data) { if (!_rs232Port.waitForBytesWritten(WRITE_TIMEOUT.count())) { - if ( _rs232Port.error() == QSerialPort::TimeoutError ) + if (_rs232Port.error() == QSerialPort::TimeoutError) { - Debug(_log, "Timeout after %dms: %d frames already dropped", WRITE_TIMEOUT.count(), _frameDropCounter); + Debug(_log, "Timeout after %dms: %d frames already dropped, Rs232 SerialPortError [%d]: %s", WRITE_TIMEOUT.count(), _frameDropCounter, _rs232Port.error(), QSTRING_CSTR(_rs232Port.errorString())); ++_frameDropCounter; @@ -258,10 +258,16 @@ int ProviderRs232::writeBytes(const qint64 size, const uint8_t *data) } else { - this->setInError( QString ("Rs232 SerialPortError: %1").arg(_rs232Port.errorString()) ); + this->setInError( QString ("Error writing data to %1, Error: %2").arg(_deviceName).arg(_rs232Port.error())); rc = -1; } } + + if (rc == -1) + { + Info(_log, "Try restarting the device %s after error occured...", QSTRING_CSTR(_activeDeviceType)); + emit enable(); + } } return rc; } From a1f0821f3308671b573a09cd158db5e9ebe99fb4 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Mon, 27 Nov 2023 09:06:43 +0100 Subject: [PATCH 66/98] Introduce Event Services (#1653) * Allow to enable/disable suspend & lock event handling * Fix Windows * Refactor event handling incl.CEC * Revert "Auxiliary commit to revert individual files from 0d9a8b8a3a4a09609a339f54c7d8a9384c561282" This reverts commit 80737d926ad151a07b2493dd1685ed502975cb2e. * Support Events for Grabbers generically * Have CECEvent to actions configurable, further clean-ups * Remove handleEvent from V4L2grabber, as grabber will be stopped on suspend * Validate that one CEC Event can only trigger one action * MacOS lock/unlock added * fast windows fix * Corrections * Fix CodeQL findings * add macos lock/unlock handler * Migration of CEC-config and have default actions * Correct target_link_libraries * Include Foundation * macOS include AppKit * Support Scheduled Events, cleanups. * Fix destructing * Fix coredump during free * Consider additional error sceanrio * Fix missing code * install desktop icons * correct bash logic --------- Co-authored-by: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com> --- CHANGELOG.md | 7 + assets/webconfig/content/conf_events.html | 11 + assets/webconfig/i18n/en.json | 88 ++- assets/webconfig/index.html | 1 + assets/webconfig/js/content_events.js | 157 +++++ assets/webconfig/js/content_grabber.js | 6 - bin/scripts/install_pr.sh | 2 +- cmake/package-scripts/postinst | 18 +- cmake/package-scripts/prerm | 2 +- config/hyperion.config.json.default | 29 +- include/api/JsonAPI.h | 20 +- include/cec/CECEvent.h | 8 - include/cec/CECHandler.h | 33 +- include/db/SettingsTable.h | 2 + include/events/EventEnum.h | 50 ++ include/events/EventHandler.h | 49 ++ include/events/EventScheduler.h | 55 ++ include/events/OsEventHandler.h | 132 ++++ include/grabber/video/VideoWrapper.h | 8 - include/grabber/video/v4l2/V4L2Grabber.h | 13 +- include/hyperion/Grabber.h | 6 + include/hyperion/GrabberWrapper.h | 4 + include/hyperion/HyperionIManager.h | 33 +- include/utils/settings.h | 55 +- libsrc/CMakeLists.txt | 10 +- libsrc/api/JsonAPI.cpp | 33 +- libsrc/cec/CECHandler.cpp | 343 ++++++---- libsrc/cec/CMakeLists.txt | 1 - libsrc/events/CMakeLists.txt | 27 + libsrc/events/EventHandler.cpp | 186 ++++++ libsrc/events/EventScheduler.cpp | 153 +++++ libsrc/events/OsEventHandler.cpp | 626 ++++++++++++++++++ libsrc/grabber/video/VideoWrapper.cpp | 17 +- libsrc/grabber/video/v4l2/V4L2Grabber.cpp | 35 +- libsrc/hyperion/CMakeLists.txt | 1 + libsrc/hyperion/GrabberWrapper.cpp | 8 + libsrc/hyperion/HyperionIManager.cpp | 33 +- libsrc/hyperion/SettingsManager.cpp | 38 ++ libsrc/hyperion/hyperion.schema.json | 12 + libsrc/hyperion/resource.qrc | 4 + libsrc/hyperion/schema/schema-cecEvents.json | 95 +++ .../hyperion/schema/schema-eventActions.json | 25 + .../hyperion/schema/schema-grabberV4L2.json | 26 +- libsrc/hyperion/schema/schema-osEvents.json | 34 + .../hyperion/schema/schema-schedEvents.json | 40 ++ src/hyperiond/CMakeLists.txt | 2 +- src/hyperiond/SuspendHandler.cpp | 312 --------- src/hyperiond/SuspendHandler.h | 79 --- src/hyperiond/hyperiond.cpp | 119 ++-- src/hyperiond/hyperiond.h | 26 +- src/hyperiond/main.cpp | 36 +- src/hyperiond/systray.cpp | 12 +- src/hyperiond/systray.h | 13 +- 53 files changed, 2306 insertions(+), 829 deletions(-) create mode 100644 assets/webconfig/content/conf_events.html create mode 100644 assets/webconfig/js/content_events.js delete mode 100644 include/cec/CECEvent.h create mode 100644 include/events/EventEnum.h create mode 100644 include/events/EventHandler.h create mode 100644 include/events/EventScheduler.h create mode 100644 include/events/OsEventHandler.h create mode 100644 libsrc/events/CMakeLists.txt create mode 100644 libsrc/events/EventHandler.cpp create mode 100644 libsrc/events/EventScheduler.cpp create mode 100644 libsrc/events/OsEventHandler.cpp create mode 100644 libsrc/hyperion/schema/schema-cecEvents.json create mode 100644 libsrc/hyperion/schema/schema-eventActions.json create mode 100644 libsrc/hyperion/schema/schema-osEvents.json create mode 100644 libsrc/hyperion/schema/schema-schedEvents.json delete mode 100644 src/hyperiond/SuspendHandler.cpp delete mode 100644 src/hyperiond/SuspendHandler.h diff --git a/CHANGELOG.md b/CHANGELOG.md index bb8bbeb8..f4cde2a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New language: Hebrew +**Event Services** +Newly introduced Event Service configuration and consistent handling across all components +- Suspend/Resume & Screen Locking support for MaxOS +- Allow to enable/disable suspend & lock on operating system events (#1633, #1632) +- Scheduled events allowing to suspend,resume, etc. (#1088) +- Configurable CEC event handling + ##### LED-Devices **Philips Hue** diff --git a/assets/webconfig/content/conf_events.html b/assets/webconfig/content/conf_events.html new file mode 100644 index 00000000..df98b737 --- /dev/null +++ b/assets/webconfig/content/conf_events.html @@ -0,0 +1,11 @@ +
+
+
+ +
+
+
+
+
+ + diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 0b238935..4e8742a9 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -192,6 +192,12 @@ "conf_network_tok_intro": "Here you can create and delete tokens for API authentication. Created tokens will only be displayed once.", "conf_network_tok_lastuse": "Last use", "conf_network_tok_title": "Token Management", + "conf_cec_events_heading_title": "CEC Events", + "conf_cec_events_intro": "Settings related to diffent CEC (Consumer Electronics Control) protocol events Hyperion can handle", + "conf_os_events_heading_title": "Operating System Events", + "conf_os_events_intro": "Settings related to diffent Operating System events Hyperion can handle", + "conf_sched_events_heading_title": "Scheduled Events", + "conf_sched_events_intro": "Settings related to scheduled, i.e. time based events, which Hyperion will handle", "conf_webconfig_label_intro": "Webconfiguration settings. Edit wisely.", "dashboard_active_instance": "Selected instance", "dashboard_alert_message_confedit": "Your Hyperion configuration has been modified. To apply it, restart Hyperion.", @@ -243,6 +249,30 @@ "edt_append_pixel": "Pixel", "edt_append_s": "s", "edt_append_sdegree": "s/degree", + "edt_conf_action_title": "Action", + "edt_conf_action_expl": "Action to be applied", + "edt_conf_action_record_validation_error": "The same event can trigger only one action. Clean up Actions $1", + "edt_conf_audio_device_expl": "Selected audio input device", + "edt_conf_audio_device_title": "Audio Device", + "edt_conf_audio_effects_expl": "Select an effect on how the audio signal is transformed to", + "edt_conf_audio_effects_title": "Audio Effects", + "edt_conf_audio_effect_enum_vumeter": "VU-Meter", + "edt_conf_audio_effect_hotcolor_expl": "Hot Color", + "edt_conf_audio_effect_hotcolor_title": "Hot Color", + "edt_conf_audio_effect_multiplier_expl": "Audio Signal Value multiplier", + "edt_conf_audio_effect_multiplier_title": "Multiplier", + "edt_conf_audio_effect_safecolor_expl": "Safe Color", + "edt_conf_audio_effect_safecolor_title": "Safe Color", + "edt_conf_audio_effect_safevalue_expl": "Safe Threshold", + "edt_conf_audio_effect_safevalue_title": "Safe Threshold", + "edt_conf_audio_effect_set_defaults": "Reset to default values", + "edt_conf_audio_effect_tolerance_expl": "Tolerance used when auto calculating a signal multipler from 0-100", + "edt_conf_audio_effect_tolerance_title": "Tolerance", + "edt_conf_audio_effect_warncolor_expl": "Warning Color", + "edt_conf_audio_effect_warncolor_title": "Warning Color", + "edt_conf_audio_effect_warnvalue_expl": "Warning Threshold", + "edt_conf_audio_effect_warnvalue_title": "Warning Threshold", + "edt_conf_audio_heading_title": "Audio Capture", "edt_conf_bb_blurRemoveCnt_expl": "Number of pixels that get removed from the detected border to cut away blur.", "edt_conf_bb_blurRemoveCnt_title": "Blur pixel", "edt_conf_bb_borderFrameCnt_expl": "Number of frames before a consistent detected border is set.", @@ -258,6 +288,17 @@ "edt_conf_bb_unknownFrameCnt_title": "Unknown frames", "edt_conf_bge_heading_title": "Background Effect/Color", "edt_conf_bobls_heading_title": "Boblight Server", + "edt_conf_cec_actions_header_title": "Actions", + "edt_conf_cec_actions_header_expl": "Define which action should take place on a recognised CEC event", + "edt_conf_cec_actions_header_item_title": "Action", + "edt_conf_cec_button_release_delay_ms_title": "Button release time", + "edt_conf_cec_button_release_delay_ms_expl": "Remote button press release time", + "edt_conf_cec_button_repeat_rate_ms_title": "Button repeat rate", + "edt_conf_cec_button_repeat_rate_ms_expl": "Remote button press repeat rate", + "edt_conf_cec_double_tap_timeout_ms_title": "Button delay before repeating", + "edt_conf_cec_double_tap_timeout_ms_expl": "Remote button press delay before repeating", + "edt_conf_cec_event_title": "CEC Event", + "edt_conf_cec_event_expl": "CEC event that will trigger an action", "edt_conf_color_accuracyLevel_expl": "Level how accurate dominat colors are evaluated. A higher level creates more accurate results, but also requries more processing power. Should to be combined with reduced pixel processing.", "edt_conf_color_accuracyLevel_title": "Accuracy level", "edt_conf_color_backlightColored_expl": "Add some color to your backlight.", @@ -320,6 +361,13 @@ "edt_conf_enum_HORIZONTAL": "Horizontal", "edt_conf_enum_VERTICAL": "Vertical", "edt_conf_enum_BOTH": "Horizontal & Vertical", + "edt_conf_enum_action_idle": "Idle", + "edt_conf_enum_action_restart": "Restart", + "edt_conf_enum_action_resume": "Resume", + "edt_conf_enum_action_resumeIdle": "ResumeIdle", + "edt_conf_enum_action_suspend": "Suspend", + "edt_conf_enum_action_toggleIdle": "ToggleIdle", + "edt_conf_enum_action_toggleSuspend": "ToggleSuspend", "edt_conf_enum_automatic": "Automatic", "edt_conf_enum_bbclassic": "Classic", "edt_conf_enum_bbdefault": "Default", @@ -328,6 +376,12 @@ "edt_conf_enum_bgr": "BGR", "edt_conf_enum_bottom_up": "Bottom up", "edt_conf_enum_brg": "BRG", + "edt_conf_enum_cec_key_f1_blue": "Blue button pressed", + "edt_conf_enum_cec_key_f2_red": "Red button pressed", + "edt_conf_enum_cec_key_f3_green": "Green button pressed", + "edt_conf_enum_cec_key_f4_yellow": "Yellow button pressed", + "edt_conf_enum_cec_opcode_set stream path": "TV on", + "edt_conf_enum_cec_opcode_standby": "TV off", "edt_conf_enum_color": "Color", "edt_conf_enum_custom": "Custom", "edt_conf_enum_decay": "Decay", @@ -455,9 +509,18 @@ "edt_conf_net_localApiAuth_title": "Local API Authentication", "edt_conf_net_restirctedInternetAccessAPI_expl": "You can restrict the access to the API through the internet to certain IP's.", "edt_conf_net_restirctedInternetAccessAPI_title": "Restrict to IP's", + "edt_conf_os_events_lockEnable_title": "Listen to lock events", + "edt_conf_os_events_lockEnable_expl": "Listen to screen lock/unlock events", + "edt_conf_os_events_suspendEnable_title": "Listen to suspend events", + "edt_conf_os_events_suspendEnable_expl": "Listen to operating system suspend/resume events", + "edt_conf_os_events_suspendOnLockEnable_title": "Suspend when locked", + "edt_conf_os_events_suspendOnLockEnable_expl": "Suspend when the screen is locked, otherwise go into idle mode", "edt_conf_pbs_heading_title": "Protocol Buffers Server", "edt_conf_pbs_timeout_expl": "If no data are received for the given period, the component will be (soft) disabled.", "edt_conf_pbs_timeout_title": "Timeout", + "edt_conf_sched_actions_header_title": "Actions", + "edt_conf_sched_actions_header_expl": "Define which action should take place on a point in time. The action will be scheduled daily.", + "edt_conf_sched_actions_header_item_title": "Action", "edt_conf_smooth_continuousOutput_expl": "Update the LEDs even there is no changed picture.", "edt_conf_smooth_continuousOutput_title": "Continuous output", "edt_conf_smooth_decay_expl": "The speed of decay. 1 is linear, greater values are have stronger effect.", @@ -475,6 +538,8 @@ "edt_conf_smooth_updateDelay_title": "Output delay", "edt_conf_smooth_updateFrequency_expl": "The output speed to your LED controller.", "edt_conf_smooth_updateFrequency_title": "Update frequency", + "edt_conf_time_event_title": "Time", + "edt_conf_time_event_expl": "Point in time that will trigger an action", "edt_conf_v4l2_blueSignalThreshold_expl": "Darkens low blue values (recognized as black)", "edt_conf_v4l2_blueSignalThreshold_title": "Blue signal threshold", "edt_conf_v4l2_cecDetection_expl": "If enabled, USB capture will be temporarily disabled when CEC standby event received from HDMI bus.", @@ -534,27 +599,6 @@ "edt_conf_v4l2_hardware_set_defaults_tip": "Set device's default values for brightness, contrast, hue and saturation", "edt_conf_v4l2_noSignalCounterThreshold_title": "Signal Counter Threshold", "edt_conf_v4l2_noSignalCounterThreshold_expl": "Count of frames (check that with grabber's current FPS mode) after which the no signal is triggered", - "edt_conf_audio_device_expl": "Selected audio input device", - "edt_conf_audio_device_title": "Audio Device", - "edt_conf_audio_effects_expl": "Select an effect on how the audio signal is transformed to", - "edt_conf_audio_effects_title": "Audio Effects", - "edt_conf_audio_effect_enum_vumeter": "VU-Meter", - "edt_conf_audio_effect_hotcolor_expl": "Hot Color", - "edt_conf_audio_effect_hotcolor_title": "Hot Color", - "edt_conf_audio_effect_multiplier_expl": "Audio Signal Value multiplier", - "edt_conf_audio_effect_multiplier_title": "Multiplier", - "edt_conf_audio_effect_safecolor_expl": "Safe Color", - "edt_conf_audio_effect_safecolor_title": "Safe Color", - "edt_conf_audio_effect_safevalue_expl": "Safe Threshold", - "edt_conf_audio_effect_safevalue_title": "Safe Threshold", - "edt_conf_audio_effect_set_defaults": "Reset to default values", - "edt_conf_audio_effect_tolerance_expl": "Tolerance used when auto calculating a signal multipler from 0-100", - "edt_conf_audio_effect_tolerance_title": "Tolerance", - "edt_conf_audio_effect_warncolor_expl": "Warning Color", - "edt_conf_audio_effect_warncolor_title": "Warning Color", - "edt_conf_audio_effect_warnvalue_expl": "Warning Threshold", - "edt_conf_audio_effect_warnvalue_title": "Warning Threshold", - "edt_conf_audio_heading_title": "Audio Capture", "edt_conf_webc_crtPath_expl": "Path to the certification file (format should be PEM)", "edt_conf_webc_crtPath_title": "Certificate path", "edt_conf_webc_docroot_expl": "Local webinterface root path (just for webui developer)", @@ -983,6 +1027,8 @@ "main_menu_dashboard_token": "Dashboard", "main_menu_effect_conf_token": "Effects", "main_menu_effectsconfigurator_token": "Effects Configurator", + "main_menu_events": "Event Services", + "main_menu_event_services_token": "Event Services", "main_menu_general_conf_token": "General", "main_menu_grabber_conf_token": "Capturing Hardware", "main_menu_input_selection_token": "Input Selection", diff --git a/assets/webconfig/index.html b/assets/webconfig/index.html index 9249eb39..88bf52b3 100644 --- a/assets/webconfig/index.html +++ b/assets/webconfig/index.html @@ -280,6 +280,7 @@
  • System
  • +
    + diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 01a9f6f8..68344d73 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -117,6 +117,10 @@ "conf_leds_layout_cl_topright": "Top Right (Corner)", "conf_leds_layout_cl_vleddepth": "Vertical LED depth", "conf_leds_layout_frame": "Classic Layout (LED Frame)", + "conf_leds_layout_gapleft": "Left gap", + "conf_leds_layout_gapright": "Right gap", + "conf_leds_layout_gaptop": "Top gap", + "conf_leds_layout_gapbottom": "Bottom gap", "conf_leds_layout_generatedconf": "Generated/Current LED Configuration", "conf_leds_layout_generation_success": "LED Layout generated sucessfully", "conf_leds_layout_generation_error": "LED Layout was not generated", diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js index c457262b..0432524a 100755 --- a/assets/webconfig/js/content_leds.js +++ b/assets/webconfig/js/content_leds.js @@ -468,17 +468,18 @@ function createClassicLeds() { aceEdt.set(finalLedArray); } -function createMatrixLayout(ledshoriz, ledsvert, cabling, start, direction) { +function createMatrixLayout(ledshoriz, ledsvert, cabling, start, direction, gap) { // Big thank you to RanzQ (Juha Rantanen) from Github for this script // https://raw.githubusercontent.com/RanzQ/hyperion-audio-effects/master/matrix-config.js - var parallel = false - var leds = [] - var hblock = 1.0 / ledshoriz - var vblock = 1.0 / ledsvert + let parallel = false; + const leds = []; + + const hblock = (1.0 - gap.left - gap.right) / ledshoriz; + const vblock = (1.0 - gap.top - gap.bottom) / ledsvert; if (cabling == "parallel") { - parallel = true + parallel = true; } /** @@ -487,10 +488,10 @@ function createMatrixLayout(ledshoriz, ledsvert, cabling, start, direction) { * @param {Number} y Vertical position in matrix */ function addLed(x, y) { - var hscanMin = x * hblock - var hscanMax = (x + 1) * hblock - var vscanMin = y * vblock - var vscanMax = (y + 1) * vblock + let hscanMin = gap.left + (x * hblock); + let hscanMax = gap.left + (x + 1) * hblock; + let vscanMin = gap.top + y * vblock; + let vscanMax = gap.top + (y + 1) * vblock; hscanMin = round(hscanMin); hscanMax = round(hscanMax); @@ -502,43 +503,41 @@ function createMatrixLayout(ledshoriz, ledsvert, cabling, start, direction) { hmax: hscanMax, vmin: vscanMin, vmax: vscanMax - }) + }); } - var startYX = start.split('-') - var startX = startYX[1] === 'right' ? ledshoriz - 1 : 0 - var startY = startYX[0] === 'bottom' ? ledsvert - 1 : 0 - var endX = startX === 0 ? ledshoriz - 1 : 0 - var endY = startY === 0 ? ledsvert - 1 : 0 - var forward = startX < endX + const startYX = start.split('-'); + let startX = startYX[1] === 'right' ? ledshoriz - 1 : 0; + let startY = startYX[0] === 'bottom' ? ledsvert - 1 : 0; + let endX = startX === 0 ? ledshoriz - 1 : 0; + let endY = startY === 0 ? ledsvert - 1 : 0; + let forward = startX < endX; + let downward = startY < endY; - var downward = startY < endY - - var x, y + let x, y; if (direction === 'vertical') { for (x = startX; forward && x <= endX || !forward && x >= endX; x += forward ? 1 : -1) { for (y = startY; downward && y <= endY || !downward && y >= endY; y += downward ? 1 : -1) { - - addLed(x, y) + addLed(x, y); } if (!parallel) { - downward = !downward - var tmp = startY - startY = endY - endY = tmp + downward = !downward; + const tmp = startY; + startY = endY; + endY = tmp; } } } else { for (y = startY; downward && y <= endY || !downward && y >= endY; y += downward ? 1 : -1) { for (x = startX; forward && x <= endX || !forward && x >= endX; x += forward ? 1 : -1) { - addLed(x, y) + addLed(x, y); } if (!parallel) { - forward = !forward - var tmp = startX - startX = endX - endX = tmp + forward = !forward; + const tmp = startX; + startX = endX; + endX = tmp; } } } @@ -551,13 +550,20 @@ function createMatrixLeds() { // https://raw.githubusercontent.com/RanzQ/hyperion-audio-effects/master/matrix-config.js //get values - var ledshoriz = parseInt($("#ip_ma_ledshoriz").val()); - var ledsvert = parseInt($("#ip_ma_ledsvert").val()); - var cabling = $("#ip_ma_cabling").val(); - var direction = $("#ip_ma_direction").val(); - var start = $("#ip_ma_start").val(); + const ledshoriz = parseInt($("#ip_ma_ledshoriz").val()); + const ledsvert = parseInt($("#ip_ma_ledsvert").val()); + const cabling = $("#ip_ma_cabling").val(); + const direction = $("#ip_ma_direction").val(); + const start = $("#ip_ma_start").val(); + const gap = { + //gap values % -> float + left: parseInt($("#ip_ma_gapleft").val()) / 100, + right: parseInt($("#ip_ma_gapright").val()) / 100, + top: parseInt($("#ip_ma_gaptop").val()) / 100, + bottom: parseInt($("#ip_ma_gapbottom").val()) / 100, + }; - nonBlacklistLedArray = createMatrixLayout(ledshoriz, ledsvert, cabling, start, direction); + nonBlacklistLedArray = createMatrixLayout(ledshoriz, ledsvert, cabling, start, direction, gap); finalLedArray = blackListLeds(nonBlacklistLedArray, ledBlacklist); createLedPreview(finalLedArray); @@ -797,6 +803,35 @@ $(document).ready(function () { $('.ledMAconstr').on("change", function () { valValue(this.id, this.value, this.min, this.max); + + // top/bottom and left/right must not overlap + switch (this.id) { + case "ip_ma_gapleft": + let left = 100 - parseInt($("#ip_ma_gapright").val()); + if (this.value > left) { + $(this).val(left); + } + break; + case "ip_ma_gapright": + let right = 100 - parseInt($("#ip_ma_gapleft").val()); + if (this.value > right) { + $(this).val(right); + } + break; + case "ip_ma_gaptop": + let top = 100 - parseInt($("#ip_ma_gapbottom").val()); + if (this.value > top) { + $(this).val(top); + } + break; + case "ip_ma_gapbottom": + let bottom = 100 - parseInt($("#ip_ma_gaptop").val()); + if (this.value > bottom) { + $(this).val(bottom); + } + break; + default: + } createMatrixLeds(); }); diff --git a/cmake/package-scripts/postinst b/cmake/package-scripts/postinst index d1832220..f24e6ac6 100644 --- a/cmake/package-scripts/postinst +++ b/cmake/package-scripts/postinst @@ -26,13 +26,6 @@ install_file() echo "--- Hyperion ambient light postinstall ---" -#check system -CPU_RPI=`grep -m1 -c 'BCM2708\|BCM2709\|BCM2710\|BCM2835\|BCM2836\|BCM2837\|BCM2711' /proc/cpuinfo` -CPU_X32X64=`uname -m | grep 'x86_32\|i686\|x86_64' | wc -l` - -#Check for a bootloader as Berryboot -BOOT_BERRYBOOT=$(grep -m1 -c '\(/var/media\|/media/pi\)/berryboot' /etc/mtab) - #get current system ip NET_IP=`hostname -I | cut -d " " -f1` @@ -128,25 +121,6 @@ fi rm -r /usr/share/hyperion/desktop 2>/dev/null rm -r /usr/share/hyperion/icons 2>/dev/null -#Check, if dtparam=spi=on is in place -if [ $CPU_RPI -eq 1 ]; then - BOOT_DIR="/boot" - if [ $BOOT_BERRYBOOT -eq 1 ]; then - BOOT_DIR=$(sed -ne "s#/dev/mmcblk0p1 \([^ ]*\) vfat rw,.*#\1#p" /etc/mtab) - fi - if [ -z "$BOOT_DIR" -o ! -f "$BOOT_DIR/config.txt" ]; then - echo '---> Warning: RPi using BERRYBOOT found but can not locate where config.txt is to enable SPI. (BOOT_DIR='"$BOOT_DIR)" - SPIOK=1 # Not sure if OK, but don't ask to reboot - else - SPIOK=`grep '^\dtparam=spi=on' "$BOOT_DIR/config.txt" | wc -l` - if [ $SPIOK -ne 1 ]; then - echo '---> Raspberry Pi found, but SPI is not set, we write "dtparam=spi=on" to '"$BOOT_DIR/config.txt" - sed -i '$a dtparam=spi=on' "$BOOT_DIR/config.txt" - REBOOTMESSAGE="echo Please reboot your Raspberry Pi, we inserted dtparam=spi=on to $BOOT_DIR/config.txt" - fi - fi -fi - echo ${START_MSG} echo "-----------------------------------------------------------------------------" @@ -171,7 +145,7 @@ if [ -e /opt/hyperion/ ] then echo echo "---------------------------------------------------------------------------------" - echo "- It seemd that you have an older version of hyperion installed in /opt/hyperion -" + echo "- It seems that you have an older version of hyperion installed in /opt/hyperion -" echo "- please remove it to avoid problems -" echo "---------------------------------------------------------------------------------" fi diff --git a/include/blackborder/BlackBorderDetector.h b/include/blackborder/BlackBorderDetector.h index 77438f4c..66aaa32c 100644 --- a/include/blackborder/BlackBorderDetector.h +++ b/include/blackborder/BlackBorderDetector.h @@ -110,7 +110,7 @@ namespace hyperion } // Construct result - BlackBorder detectedBorder; + BlackBorder detectedBorder{}; detectedBorder.unknown = firstNonBlackXPixelIndex == -1 || firstNonBlackYPixelIndex == -1; detectedBorder.horizontalSize = firstNonBlackYPixelIndex; detectedBorder.verticalSize = firstNonBlackXPixelIndex; @@ -167,7 +167,7 @@ namespace hyperion } // Construct result - BlackBorder detectedBorder; + BlackBorder detectedBorder{}; detectedBorder.unknown = firstNonBlackXPixelIndex == -1 || firstNonBlackYPixelIndex == -1; detectedBorder.horizontalSize = firstNonBlackYPixelIndex; detectedBorder.verticalSize = firstNonBlackXPixelIndex; @@ -224,7 +224,7 @@ namespace hyperion } // Construct result - BlackBorder detectedBorder; + BlackBorder detectedBorder{}; detectedBorder.unknown = firstNonBlackXPixelIndex == -1 || firstNonBlackYPixelIndex == -1; detectedBorder.horizontalSize = firstNonBlackYPixelIndex; detectedBorder.verticalSize = firstNonBlackXPixelIndex; @@ -267,7 +267,7 @@ namespace hyperion } // Construct result - BlackBorder detectedBorder; + BlackBorder detectedBorder{}; detectedBorder.unknown = firstNonBlackYPixelIndex == -1; detectedBorder.horizontalSize = firstNonBlackYPixelIndex; detectedBorder.verticalSize = 0; diff --git a/libsrc/hyperion/schema/schema-ledConfig.json b/libsrc/hyperion/schema/schema-ledConfig.json index 3232063c..3a3c6809 100644 --- a/libsrc/hyperion/schema/schema-ledConfig.json +++ b/libsrc/hyperion/schema/schema-ledConfig.json @@ -144,6 +144,30 @@ "start": { "type": "string", "enum": [ "top-left", "top-right", "bottom-left", "bottom-right" ] + }, + "gapleft": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "default": 0 + }, + "gapright": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "default": 0 + }, + "gaptop": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "default": 0 + }, + "gapbottom": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "default": 0 } }, "additionalProperties": false From 806206ec012deb803c89e6ad8a9976c6bfb933dd Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Fri, 15 Mar 2024 20:23:53 +0100 Subject: [PATCH 98/98] Fix #1714 (#1717) --- include/grabber/amlogic/AmlogicWrapper.h | 9 ++++----- libsrc/grabber/amlogic/AmlogicWrapper.cpp | 7 ++++--- libsrc/grabber/directx/DirectXWrapper.cpp | 7 +++++-- libsrc/grabber/dispmanx/DispmanxWrapper.cpp | 3 ++- libsrc/grabber/framebuffer/FramebufferWrapper.cpp | 6 ++++-- libsrc/grabber/osx/OsxWrapper.cpp | 6 ++++-- libsrc/grabber/qt/QtWrapper.cpp | 7 +++++-- libsrc/grabber/x11/X11Wrapper.cpp | 8 +++++--- libsrc/grabber/xcb/XcbWrapper.cpp | 4 +++- 9 files changed, 36 insertions(+), 21 deletions(-) diff --git a/include/grabber/amlogic/AmlogicWrapper.h b/include/grabber/amlogic/AmlogicWrapper.h index 3f763f5c..5e355dff 100644 --- a/include/grabber/amlogic/AmlogicWrapper.h +++ b/include/grabber/amlogic/AmlogicWrapper.h @@ -18,12 +18,11 @@ public: /// /// Constructs the Amlogic frame grabber /// - /// @param[in] grabWidth The width of the grabbed image [pixels] - /// @param[in] grabHeight The height of the grabbed images [pixels] - /// @param[in] pixelDecimation Decimation factor for image [pixels] + /// @param[in] updateRate_Hz The image grab rate [Hz] + /// @param[in] pixelDecimation Decimation factor for image [pixels]/// /// - AmlogicWrapper(int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION, - int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ); + AmlogicWrapper(int updateRate_Hz=GrabberWrapper::DEFAULT_RATE_HZ, + int pixelDecimation=GrabberWrapper::DEFAULT_PIXELDECIMATION); /// /// Constructs the Amlogic frame grabber from configuration settings diff --git a/libsrc/grabber/amlogic/AmlogicWrapper.cpp b/libsrc/grabber/amlogic/AmlogicWrapper.cpp index d568e999..6c3b302c 100644 --- a/libsrc/grabber/amlogic/AmlogicWrapper.cpp +++ b/libsrc/grabber/amlogic/AmlogicWrapper.cpp @@ -1,14 +1,15 @@ #include -AmlogicWrapper::AmlogicWrapper(int pixelDecimation, int updateRate_Hz) +AmlogicWrapper::AmlogicWrapper(int updateRate_Hz, int pixelDecimation) : GrabberWrapper(GRABBERTYPE, &_grabber, updateRate_Hz) - , _grabber() + , _grabber() { _grabber.setPixelDecimation(pixelDecimation); } AmlogicWrapper::AmlogicWrapper(const QJsonDocument& grabberConfig) - : GrabberWrapper(GRABBERTYPE, &_grabber) + : AmlogicWrapper(GrabberWrapper::DEFAULT_RATE_HZ, + GrabberWrapper::DEFAULT_PIXELDECIMATION) { this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); } diff --git a/libsrc/grabber/directx/DirectXWrapper.cpp b/libsrc/grabber/directx/DirectXWrapper.cpp index c0ba4ecc..1d5d905e 100644 --- a/libsrc/grabber/directx/DirectXWrapper.cpp +++ b/libsrc/grabber/directx/DirectXWrapper.cpp @@ -6,14 +6,17 @@ DirectXWrapper::DirectXWrapper( int updateRate_Hz, int cropLeft, int cropRight, int cropTop, int cropBottom ) : GrabberWrapper(GRABBERTYPE, &_grabber, updateRate_Hz) - , _grabber(display, cropLeft, cropRight, cropTop, cropBottom) + , _grabber(display, cropLeft, cropRight, cropTop, cropBottom) { _grabber.setPixelDecimation(pixelDecimation); } DirectXWrapper::DirectXWrapper(const QJsonDocument& grabberConfig) - : GrabberWrapper(GRABBERTYPE, &_grabber) + : DirectXWrapper(GrabberWrapper::DEFAULT_RATE_HZ, + 0, + GrabberWrapper::DEFAULT_PIXELDECIMATION, + 0,0,0,0) { this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); } diff --git a/libsrc/grabber/dispmanx/DispmanxWrapper.cpp b/libsrc/grabber/dispmanx/DispmanxWrapper.cpp index 32824762..0194a4f1 100644 --- a/libsrc/grabber/dispmanx/DispmanxWrapper.cpp +++ b/libsrc/grabber/dispmanx/DispmanxWrapper.cpp @@ -12,7 +12,8 @@ DispmanxWrapper::DispmanxWrapper( int updateRate_Hz, } DispmanxWrapper::DispmanxWrapper(const QJsonDocument& grabberConfig) - : GrabberWrapper(GRABBERTYPE, &_grabber) + : DispmanxWrapper(GrabberWrapper::DEFAULT_RATE_HZ, + GrabberWrapper::DEFAULT_PIXELDECIMATION) { this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); } diff --git a/libsrc/grabber/framebuffer/FramebufferWrapper.cpp b/libsrc/grabber/framebuffer/FramebufferWrapper.cpp index 2588afed..1797e513 100644 --- a/libsrc/grabber/framebuffer/FramebufferWrapper.cpp +++ b/libsrc/grabber/framebuffer/FramebufferWrapper.cpp @@ -4,13 +4,15 @@ FramebufferWrapper::FramebufferWrapper( int updateRate_Hz, int deviceIdx, int pixelDecimation) : GrabberWrapper(GRABBERTYPE, &_grabber, updateRate_Hz) - , _grabber(deviceIdx) + , _grabber(deviceIdx) { _grabber.setPixelDecimation(pixelDecimation); } FramebufferWrapper::FramebufferWrapper(const QJsonDocument& grabberConfig) - : GrabberWrapper(GRABBERTYPE, &_grabber) + : FramebufferWrapper(GrabberWrapper::DEFAULT_RATE_HZ, + 0, + GrabberWrapper::DEFAULT_PIXELDECIMATION) { this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); } diff --git a/libsrc/grabber/osx/OsxWrapper.cpp b/libsrc/grabber/osx/OsxWrapper.cpp index 86058d4f..d9bb51b8 100644 --- a/libsrc/grabber/osx/OsxWrapper.cpp +++ b/libsrc/grabber/osx/OsxWrapper.cpp @@ -5,13 +5,15 @@ OsxWrapper::OsxWrapper( int updateRate_Hz, int pixelDecimation ) : GrabberWrapper(GRABBERTYPE, &_grabber, updateRate_Hz) - , _grabber(display) + , _grabber(display) { _grabber.setPixelDecimation(pixelDecimation); } OsxWrapper::OsxWrapper(const QJsonDocument& grabberConfig) - : GrabberWrapper(GRABBERTYPE, &_grabber) + : OsxWrapper(GrabberWrapper::DEFAULT_RATE_HZ, + kCGDirectMainDisplay, + GrabberWrapper::DEFAULT_PIXELDECIMATION) { this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); } diff --git a/libsrc/grabber/qt/QtWrapper.cpp b/libsrc/grabber/qt/QtWrapper.cpp index d4560676..1cb8287d 100644 --- a/libsrc/grabber/qt/QtWrapper.cpp +++ b/libsrc/grabber/qt/QtWrapper.cpp @@ -6,13 +6,16 @@ QtWrapper::QtWrapper( int updateRate_Hz, int cropLeft, int cropRight, int cropTop, int cropBottom ) : GrabberWrapper(GRABBERTYPE, &_grabber, updateRate_Hz) - , _grabber(display, cropLeft, cropRight, cropTop, cropBottom) + , _grabber(display, cropLeft, cropRight, cropTop, cropBottom) { _grabber.setPixelDecimation(pixelDecimation); } QtWrapper::QtWrapper(const QJsonDocument& grabberConfig) - : GrabberWrapper(GRABBERTYPE, &_grabber) + : QtWrapper(GrabberWrapper::DEFAULT_RATE_HZ, + 0, + GrabberWrapper::DEFAULT_PIXELDECIMATION, + 0,0,0,0) { this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); } diff --git a/libsrc/grabber/x11/X11Wrapper.cpp b/libsrc/grabber/x11/X11Wrapper.cpp index d8df3cee..df2c5c51 100644 --- a/libsrc/grabber/x11/X11Wrapper.cpp +++ b/libsrc/grabber/x11/X11Wrapper.cpp @@ -4,14 +4,16 @@ X11Wrapper::X11Wrapper( int updateRate_Hz, int pixelDecimation, int cropLeft, int cropRight, int cropTop, int cropBottom) : GrabberWrapper(GRABBERTYPE, &_grabber, updateRate_Hz) - , _grabber(cropLeft, cropRight, cropTop, cropBottom) - , _init(false) + , _grabber(cropLeft, cropRight, cropTop, cropBottom) + , _init(false) { _grabber.setPixelDecimation(pixelDecimation); } X11Wrapper::X11Wrapper(const QJsonDocument& grabberConfig) - : GrabberWrapper(GRABBERTYPE, &_grabber) + : X11Wrapper(GrabberWrapper::DEFAULT_RATE_HZ, + GrabberWrapper::DEFAULT_PIXELDECIMATION, + 0,0,0,0) { this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); } diff --git a/libsrc/grabber/xcb/XcbWrapper.cpp b/libsrc/grabber/xcb/XcbWrapper.cpp index aa3db000..c9d4abd4 100644 --- a/libsrc/grabber/xcb/XcbWrapper.cpp +++ b/libsrc/grabber/xcb/XcbWrapper.cpp @@ -11,7 +11,9 @@ XcbWrapper::XcbWrapper( int updateRate_Hz, } XcbWrapper::XcbWrapper(const QJsonDocument& grabberConfig) - : GrabberWrapper(GRABBERTYPE, &_grabber) + : XcbWrapper(GrabberWrapper::DEFAULT_RATE_HZ, + GrabberWrapper::DEFAULT_PIXELDECIMATION, + 0,0,0,0) { this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); }