diff --git a/.github/workflows/apt.yml b/.github/workflows/apt.yml index 74de0a7c..3677fe2b 100644 --- a/.github/workflows/apt.yml +++ b/.github/workflows/apt.yml @@ -35,7 +35,7 @@ jobs: - name: Build package shell: bash run: | - tr -d '\n' < version > temp && mv temp version + tr -d '\n' < .version > temp && mv temp .version mkdir -p "${GITHUB_WORKSPACE}/deploy" docker run --rm \ -v "${GITHUB_WORKSPACE}/deploy:/deploy" \ @@ -43,7 +43,7 @@ jobs: ghcr.io/hyperion-project/${{ matrix.architecture }}:$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') \ /bin/bash -c "cd /source && \ mkdir -p debian/source && echo '3.0 (quilt)' > debian/source/format && \ - dch --create --distribution $(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') --package 'hyperion' -v '$(cat version)~$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]')' '${{ github.event.commits[0].message }}' && \ + dch --create --distribution $(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') --package 'hyperion' -v '$(cat .version)~$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]')' '${{ 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' debian/control.in > debian/control && \ tar cf ../hyperion_2.0.0.orig.tar . && \ diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 52ab9063..4e3f9fd3 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -36,12 +36,12 @@ jobs: with: submodules: true - # Append PR number to version + # 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 + tr -d '\n' < .version > temp && mv temp .version + echo -n "+PR${{ github.event.pull_request.number }}" >> .version # Build packages - name: Build packages @@ -82,12 +82,12 @@ jobs: with: submodules: true - # Append PR number to version + # 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 + tr -d '\n' < .version > temp && mv temp .version + echo -n "+PR${{ github.event.pull_request.number }}" >> .version # Install dependencies - name: Install dependencies @@ -131,12 +131,12 @@ jobs: with: submodules: true - # Append PR number to version + # 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 + tr -d '\n' < .version > temp && mv temp .version + echo -n "+PR${{ github.event.pull_request.number }}" >> .version - name: Cache Qt uses: actions/cache@v2 @@ -199,27 +199,3 @@ jobs: with: name: windows path: windows - -########################## -#### Snap (x86_64) ####### -########################## - - snap: - name: Snap (x86_64) - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Build snap package - - name: Build snap - id: build - uses: snapcore/action-build@v1 - - # Upload snap artifact (only on tagged commit) - - name: Upload snap artifact - uses: actions/upload-artifact@v2 - with: - name: snap - path: ${{ steps.build.outputs.snap }} diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index 8596b2c9..ce46c764 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -156,31 +156,6 @@ jobs: with: path: build/Hyperion-* -########################## -#### Snap (x86_64) ####### -########################## - - snap: - name: Snap (x86_64) - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Build snap package - - name: Build snap - id: build - uses: snapcore/action-build@v1 - - # Upload snap artifact (only on tagged commit) - - name: Upload snap artifact - if: startsWith(github.event.ref, 'refs/tags') - uses: actions/upload-artifact@v2 - with: - name: snap - path: ${{ steps.build.outputs.snap }} - ####################################### ###### Publish GitHub Releases ######## ####################################### @@ -195,10 +170,10 @@ jobs: uses: actions/checkout@v2 # generate environment variables - - name: Generate environment variables from version and tag + - 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 + echo "VERSION=$(tr -d '\n' < .version)" >> $GITHUB_ENV echo "preRelease=false" >> $GITHUB_ENV # If version contains alpha or beta, mark draft release as pre-release @@ -223,35 +198,3 @@ jobs: prerelease: ${{ env.preRelease }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - -############################ -###### Snap Release ######## -############################ - - snap_publish: - name: Publish Snap Release - if: startsWith(github.event.ref, 'refs/tags') - needs: [snap] - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - # Download snap from snap job - - name: Download snap from snap build - id: download-artifact - uses: actions/download-artifact@v2 - with: - name: snap - - # Get file name of the snap - - name: Get file name of the snap - run: echo "snap=$(ls ${{ steps.download-artifact.outputs.download-path }}/hyperion-ng_*.snap)" >> $GITHUB_ENV - - # Publish snap build to edge channel - - name: Publish snap build to edge channel - uses: snapcore/action-publish@v1 - with: - store_login: ${{ secrets.SNAP_STORE_LOGIN }} - snap: ${{ env.snap }} - release: edge diff --git a/version b/.version similarity index 100% rename from version rename to .version diff --git a/CMakeLists.txt b/CMakeLists.txt index 7241f354..af5890e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ PROJECT(hyperion) # Parse semantic version of version file and write version to config include (${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cmake) -file (STRINGS "version" HYPERION_VERSION) +file (STRINGS ".version" HYPERION_VERSION) SetVersionNumber(HYPERION ${HYPERION_VERSION}) set(DEFAULT_JSON_CONFIG_FILE ${CMAKE_CURRENT_SOURCE_DIR}/config/hyperion.config.json.default) file(READ ${DEFAULT_JSON_CONFIG_FILE} DEFAULT_JSON_CONFIG_VAR) diff --git a/assets/webconfig/css/darkMode.css b/assets/webconfig/css/darkMode.css index 395df0f8..d4873eac 100644 --- a/assets/webconfig/css/darkMode.css +++ b/assets/webconfig/css/darkMode.css @@ -299,6 +299,10 @@ select.form-control { color: #DDDDDD; } +.radio__field:checked ~ .radio__icon::before { + background: #fff; + } + .btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle) { border-bottom-right-radius: 4px; border-top-right-radius: 4px; diff --git a/assets/webconfig/css/hyperion.css b/assets/webconfig/css/hyperion.css index 3e9f7fea..a239a23f 100644 --- a/assets/webconfig/css/hyperion.css +++ b/assets/webconfig/css/hyperion.css @@ -764,6 +764,88 @@ li a:active:after { padding: 15px; } +/*https://github.com/json-editor/json-editor/blob/2e005a2bd34c05803702d8bc1347efde7a4926ce/docs/radio.html#L37 radiobox for Json-Editor*/ +.radio { + position: relative; + display: inline-block; + min-width: 1.625rem; + min-height: 1.625rem; + max-width: 100%; +} + +.radio:first-child { + margin-right: 20px; +} + +.radio__field { + display: none; +} + +.radio__icon { + position: absolute; + top: -0.125rem; + left: -0.125rem; + width: 1.875rem; + height: 1.875rem; + -webkit-box-sizing: border-box; + box-sizing: border-box; + border: 0.125rem solid #616161; + border-radius: 50%; + cursor: pointer; +} + +.radio__icon::before { + position: absolute; + content: ''; + top: 50%; + left: 50%; + width: 0.75rem; + height: 0.75rem; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + border-radius: 50%; + visibility: hidden; +} + +.radio__icon::after { + position: absolute; + content: ''; + top: -0.4375rem; + right: -0.4375rem; + bottom: -0.4375rem; + left: -0.4375rem; +} + +.radio__label { + line-height: 1.5rem; + display: block; + padding: 0.0625rem 0 0 2.375rem; + cursor: pointer; +} + +.radio__field:checked ~ .radio__icon::before { + background: #616161; + visibility: visible; +} + +/* disabled state css */ +.radio__field:disabled:checked ~ .radio__icon, +.radio__field:disabled:not(:checked) ~ .radio__icon { + cursor: default; + border-color: #959899; +} + +.radio__field:disabled:checked ~ .radio__label, +.radio__field:disabled:not(:checked) ~ .radio__label { + cursor: default; + color: #959899; +} + +.radio__field:disabled:checked ~ .radio__icon::before, +.radio__field:disabled:not(:checked) ~ .radio__icon::before { + cursor: default; + background-color: #959899; +} /*https://github.com/flatlogic/awesome-bootstrap-checkbox slighty edited for our purposes*/ .checkbox { diff --git a/assets/webconfig/i18n/de.json b/assets/webconfig/i18n/de.json index d6e69702..ff7cb87e 100644 --- a/assets/webconfig/i18n/de.json +++ b/assets/webconfig/i18n/de.json @@ -636,12 +636,17 @@ "edt_eff_flag_header": "Flaggen", "edt_eff_flag_header_desc": "Verpasse deinen LEDs die Farben deines Landes. Du kannst mehr als eine Flagge auswählen, je nach Intervall werden diese dann abwechselnd angezeigt.", "edt_eff_fps": "Bilder pro Sekunde", + "edt_eff_grayscale": "Graustufen", "edt_eff_frequency": "Frequenz", "edt_eff_gif_header": "GIFs", "edt_eff_gif_header_desc": "Dieser Effekt spielt .gif Dateien ab. Bietet die Möglichkeit kleine GIF-Videos abzuspielen.", "edt_eff_height": "Höhe", "edt_eff_huechange": "Farbänderung", + "edt_eff_image_source": "Bildquelle", + "edt_eff_image_source_file": "Lokale Datei", + "edt_eff_image_source_url": "URL", "edt_eff_image": "Bilddatei", + "edt_eff_url": "Bildadresse", "edt_eff_initial_blink": "Blinken beim Start", "edt_eff_interval": "Intervall", "edt_eff_knightrider_header": "Knight Rider", @@ -1018,4 +1023,4 @@ "wiz_yeelight_intro1": "Dieser Assistent hilft dir bei der Konfiguration von Hyperion für Yeelight. Zu den Funktionen zählen ein automatisches Finden der Yeelights, die einzelnen Lampen unterschiedlichen Bereichen im Bild zuzuordnen und weitere Einstellungen von Hyperion automatisch anzupassen. Kurz gesagt: Komplette Einrichtung mit ein paar Klicks.", "wiz_yeelight_title": "Yeelight Einrichtungsassistent", "wiz_yeelight_unsupported": "Nicht unterstützt" -} \ No newline at end of file +} diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index f35b6fd0..58ea4e79 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -643,11 +643,16 @@ "edt_eff_flag_header_desc": "Let your LEDs shine bright in the colours of your country. You can select more than one flag and they will change based on the interval time.", "edt_eff_fps": "Frames per seconds", "edt_eff_frequency": "Frequency", + "edt_eff_grayscale": "Grayscale", "edt_eff_gif_header": "GIF's", "edt_eff_gif_header_desc": "This effect plays .gif files, provide a simple video like loop as effect.", "edt_eff_height": "Height", "edt_eff_huechange": "Color change", + "edt_eff_image_source": "Image source", + "edt_eff_image_source_file": "Local file", + "edt_eff_image_source_url": "URL", "edt_eff_image": "Image file", + "edt_eff_url": "Image adress", "edt_eff_initial_blink" : "Flash for attention", "edt_eff_interval": "Interval", "edt_eff_knightrider_header": "Knight Rider", diff --git a/assets/webconfig/js/content_effectsconfigurator.js b/assets/webconfig/js/content_effectsconfigurator.js index 74dc16c2..0278a8e9 100644 --- a/assets/webconfig/js/content_effectsconfigurator.js +++ b/assets/webconfig/js/content_effectsconfigurator.js @@ -55,7 +55,11 @@ $(document).ready(function () { testrun = true; var args = effects_editor.getEditor('root.args'); - requestTestEffect(effectName, effectPyScript, JSON.stringify(args.getValue()), imageData); + if ($('input[type=radio][value=url]').is(':checked')) { + requestTestEffect(effectName, effectPyScript, JSON.stringify(args.getValue()), ""); + } else { + requestTestEffect(effectName, effectPyScript, JSON.stringify(args.getValue()), imageData); + } }; // Specify upload handler for image files @@ -133,7 +137,12 @@ $(document).ready(function () { // Save Effect $('#btn_write').off().on('click', function () { - requestWriteEffect(effectName, effectPyScript, JSON.stringify(effects_editor.getValue()), imageData); + if ($('input[type=radio][value=url]').is(':checked')) { + requestWriteEffect(effectName, effectPyScript, JSON.stringify(effects_editor.getValue()), ""); + } else { + requestWriteEffect(effectName, effectPyScript, JSON.stringify(effects_editor.getValue()), imageData); + } + $(window.hyperion).one("cmd-create-effect", function (event) { if (event.response.success) showInfoDialog('success', "", $.i18n('infoDialog_effconf_created_text', effectName)); diff --git a/assets/webconfig/js/lib/jsoneditor.js b/assets/webconfig/js/lib/jsoneditor.js index 7ee0fd1c..1a490f40 100755 --- a/assets/webconfig/js/lib/jsoneditor.js +++ b/assets/webconfig/js/lib/jsoneditor.js @@ -1421,7 +1421,7 @@ JSONEditor.AbstractEditor = Class.extend({ this.template_engine = this.jsoneditor.template; this.iconlib = this.jsoneditor.iconlib; this.access = this.jsoneditor.access; - + this.translate = this.jsoneditor.translate || JSONEditor.defaults.translate; this.original_schema = options.schema; @@ -1447,7 +1447,7 @@ JSONEditor.AbstractEditor = Class.extend({ if (!deps) { return; } - + var self = this; Object.keys(deps).forEach(function(dependency) { var path = self.path.split('.'); @@ -1464,13 +1464,13 @@ JSONEditor.AbstractEditor = Class.extend({ if (this.path === path || !wrapper) { return; } - + var self = this; var editor = this.jsoneditor.getEditor(path); var value = editor ? editor.getValue() : undefined; var previousStatus = this.dependenciesFulfilled; this.dependenciesFulfilled = false; - + if (!editor || !editor.dependenciesFulfilled) { this.dependenciesFulfilled = false; } else if (Array.isArray(choices)) { @@ -1504,7 +1504,7 @@ JSONEditor.AbstractEditor = Class.extend({ this.dependenciesFulfilled = !value; } } - + if (this.dependenciesFulfilled !== previousStatus) { this.notify(); } @@ -1535,7 +1535,7 @@ JSONEditor.AbstractEditor = Class.extend({ this.updateHeaderText(); this.register(); this.onWatchedFieldChange(); - + //hide input fields, if they didn't match the current access level var storedAccess = this.access if(this.schema.access){ @@ -1544,12 +1544,12 @@ JSONEditor.AbstractEditor = Class.extend({ else if(this.schema.access == 'advanced' && storedAccess == 'default') { this.container.style.display = "none"; - } + } else if(this.schema.access == 'expert' && storedAccess != 'expert') { this.container.style.display = "none"; //this.disable(); - } + } } }, @@ -2046,7 +2046,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({ if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend()); this.placeholder = this.schema.default; - + this.format = this.schema.format; if(!this.format && this.schema.media && this.schema.media.type) { this.format = this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,''); @@ -2057,7 +2057,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({ if(this.options.format) { this.format = this.options.format; } - + // Specific format if(this.format) { // Text Area @@ -2146,7 +2146,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({ } // Number or integer adds html5 tag 'number' else if (this.schema.type == "number" || this.schema.type == "integer"){ - + var min = this.schema.minimum var max = this.schema.maximum var step = this.schema.step @@ -2240,7 +2240,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({ if(this.format) this.input.setAttribute('data-schemaformat',this.format); if(this.defaultValue) this.input.setAttribute('data-schemaformat',this.format); if(this.formname && this.label)this.label.setAttribute('for',this.formname); - + this.control = this.theme.getFormControl(this.label, this.input, this.description, this.append, this.placeholder); this.container.appendChild(this.control); @@ -5104,7 +5104,7 @@ JSONEditor.defaults.editors.select = JSONEditor.AbstractEditor.extend({ if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle()); if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description); if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend()); - + if(this.options.compact) this.container.className += ' compact'; this.input = this.theme.getSelectInput(this.enum_options); @@ -5122,7 +5122,7 @@ JSONEditor.defaults.editors.select = JSONEditor.AbstractEditor.extend({ }); if(this.formname)this.label.setAttribute('for',this.formname); - + this.control = this.theme.getFormControl(this.label, this.input, this.description); this.container.appendChild(this.control); @@ -6227,6 +6227,121 @@ JSONEditor.defaults.editors.arraySelectize = JSONEditor.AbstractEditor.extend({ } }); +// Imported from Version 1.4.0.beta.0 | https://cdn.jsdelivr.net/npm/@json-editor/json-editor@1.4.0-beta.0/dist/jsoneditor.js +JSONEditor.defaults.editors.radio = JSONEditor.defaults.editors.string.extend({ + build: function () { + var self = this; + + if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle()); + if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description); + if(this.options.infoText) this.infoButton = this.theme.getInfoButton(this.options.infoText); + if(this.options.compact) this.container.classList.add('compact'); + + this.radioContainer = document.createElement('div'); + + this.enum_values = this.schema.enum; + this.enum_titles = this.options.enum_titles || []; + this.radioGroup = []; + + var radioInputEventhandler = function(e) { + e.preventDefault(); + e.stopPropagation(); + self.setValue(this.value); + self.onChange(true); + }; + + for(var i = 0; i < this.enum_values.length; i++) { + + var id = this.key + '-' + i; + + // form radio elements + var radioInput = this.theme.getFormInputField('radio'); + radioInput.name = this.formname; + radioInput.value = this.enum_values[i]; + radioInput.id = id; + radioInput.classList.add('radio__field'); + radioInput.addEventListener('change', radioInputEventhandler, false); + this.radioGroup.push(radioInput); + + // form-label for radio elements + var radioLabel = document.createElement('label'); + radioLabel.htmlFor = id; + radioLabel.classList.add('radio'); + + // contains the displayed text to the label + var radioLabelText = document.createElement('span'); + radioLabelText.innerText = $.i18n(this.options.enum_titles[i]) || this.enum_values[i]; + radioLabelText.classList.add('radio__label'); + + // permits the addition of styles for the radio itself (if you want it to look differently than browser default) + var radioLabelIcon = document.createElement('span'); + radioLabelIcon.classList.add('radio__icon'); + + radioLabel.appendChild(radioInput); + radioLabel.appendChild(radioLabelIcon); + radioLabel.appendChild(radioLabelText); + + this.radioContainer.appendChild(radioLabel); + } + + if(this.schema.readOnly || this.schema.readonly) { + this.always_disabled = true; + for (var j = 0; j < this.radioGroup.length; j++) { + this.radioGroup[j].disabled = true; + } + this.radioContainer.classList.add('readonly'); + } + + var radioContainerWrapper = this.theme.getContainer(); + radioContainerWrapper.appendChild(this.radioContainer); + + this.input = radioContainerWrapper; + + this.control = this.theme.getFormControl(this.label, radioContainerWrapper, this.description, this.infoButton); + this.container.appendChild(this.control); + }, + enable: function() { + if(!this.always_disabled) { + for (var i = 0; i/dev/null 2>&1; then + echo "The given username \"${USERNAME}\" does not exist. Exiting..." + exit 99 +fi + +echo "Configure the hyperion daemon to be executed under user: ${USERNAME}" + +if [ ${USERNAME} == "root" ]; then + echo '' + echo 'You asked to run Hyperion with root privileges. This poses a security risk!' + echo 'It is recommended not to do so unless there are good reasons (e.g. WS281x usage).' + if prompt 'Are you sure you want to run Hyperion under root? [Yes/No]'; then + echo 'No updates will be done. Exiting...' + exit 99 + fi +fi + +#Disable current service +CURRENT_SERVICE=$(systemctl --type service | { grep -o "hyperion.*@.*\.service" || true; }) +if [[ ! -z ${CURRENT_SERVICE} ]]; then + CURRENT_SERVICE_USER=$(expr "${CURRENT_SERVICE}" : 'hyperion.*@\(.*\).service') + if [ "${USERNAME}" == "${CURRENT_SERVICE_USER}" ]; then + echo "Hyperion is already running under the user: ${USERNAME}. No updates required." + exit 0; + fi + echo "Disable current service: ${CURRENT_SERVICE}" + systemctl is-active --quiet ${CURRENT_SERVICE} && systemctl disable --quiet ${CURRENT_SERVICE} --now >/dev/null 2>&1 +fi + +HYPERION="hyperion" +#Downward compatibility +if [[ ${CURRENT_SERVICE} == hyperiond* ]]; then + HYPERION="hyperiond" +fi + +#Enable new service +NEW_SERVICE="${HYPERION}@${USERNAME}.service" +echo "Restarting Hyperion Service: ${NEW_SERVICE}" +systemctl enable --quiet ${NEW_SERVICE} --now >/dev/null 2>&1 + +# Update HyperBian splash screen +sed -i "s/${CURRENT_SERVICE}/${NEW_SERVICE}/" /etc/update-motd.d/10-hyperbian >/dev/null 2>&1 + +echo "Done." +exit 0 + diff --git a/bin/service/hyperion.systemd b/bin/service/hyperion.systemd index 35817d1b..adee0eed 100644 --- a/bin/service/hyperion.systemd +++ b/bin/service/hyperion.systemd @@ -1,6 +1,10 @@ [Unit] Description=Hyperion ambient light systemd service for user %i -After=network.target +Documentation=https://docs.hyperion-project.org +Requisite=network.target +Wants=network-online.target +After=network-online.target +After=systemd-resolved.service [Service] ExecStart=/usr/bin/hyperiond diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 7efc183d..378c21da 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -158,12 +158,12 @@ macro(DeployUnix TARGET) endif() - # Copy Python modules to 'share/hyperion/lib/python' and ignore the unnecessary stuff listed below + # Copy Python modules to 'share/hyperion/lib/pythonXX' and ignore the unnecessary stuff listed below if (PYTHON_MODULES_DIR) install( DIRECTORY ${PYTHON_MODULES_DIR}/ - DESTINATION "share/hyperion/lib/python" + DESTINATION "share/hyperion/lib/python${PYTHON_VERSION_MAJOR_MINOR}" COMPONENT "Hyperion" PATTERN "*.pyc" EXCLUDE # compiled bytecodes PATTERN "__pycache__" EXCLUDE # any cache diff --git a/cmake/debian/preinst b/cmake/debian/preinst deleted file mode 100644 index 4aa8f037..00000000 --- a/cmake/debian/preinst +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/sh - -echo "---Hyperion ambient light preinst ---" - -# search for users in system, returns first entry -FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" - -# stop running daemon before we install -if pgrep hyperiond > /dev/null 2>&1 -then - if grep -m1 systemd /proc/1/comm > /dev/null - then - echo "--> stop init deamon: systemd" - # systemd - systemctl stop hyperion hyperiond"@${FOUND_USR}" 2> /dev/null - - elif [ -e /sbin/initctl ] - then - echo "--> stop init deamon: upstart" - # upstart - initctl stop hyperiond - - else - echo "--> stop init deamon: sysV" - # sysV - service hyperiond stop 2>/dev/null - fi -fi - -# In case we don't use a service kill all instances -killall hyperiond 2> /dev/null - -# overwrite last return code -exit 0 - -#$USR=hyperionIS; - -#addToGroup() -##{ -# getent group $1 && adduser $USR $1; -#} - -#check if user exists -#if id $USR >/dev/null 2>&1; then -# echo "--> hyperion user exists, skip creation"; -#else - ## create user -# echo "--> Create Hyperion user"; -# adduser --system --group $USR; -#fi - -# add user to groups if required -## secondary user groups that are required to access system things -#addToGroup(dialout); -#addToGroup(video); -#addToGroup(audio); -#addToGroup(systemd-journal); -# platform specific groups -#addToGroup(i2c); -#addToGroup(spi); -#addToGroup(gpio); diff --git a/cmake/debian/postinst b/cmake/package-scripts/postinst similarity index 50% rename from cmake/debian/postinst rename to cmake/package-scripts/postinst index 2a1100e4..1ec4121e 100644 --- a/cmake/debian/postinst +++ b/cmake/package-scripts/postinst @@ -1,5 +1,14 @@ #!/bin/sh +echo "--- postinst called with args= " $1 $2 + +# If $1=configure and $2 is set, this is an upgrade +if [ "$1" = configure ] && [ "$2" != "" ]; then + IS_UPGRADE=true +else + IS_UPGRADE=false +fi + install_file() { src="$1" @@ -15,13 +24,11 @@ install_file() fi } - 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` -OS_HYPERBIAN=`grep ID /etc/os-release | grep -m1 -c HyperBian` #Check for a bootloader as Berryboot BOOT_BERRYBOOT=$(grep -m1 -c '\(/var/media\|/media/pi\)/berryboot' /etc/mtab) @@ -32,54 +39,38 @@ NET_IP=`hostname -I | cut -d " " -f1` # search for users in system, returns first entry FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" -# determine if we should use a service -ENABLE_SERVICE=0 -STARTUP_MSG="echo ---> You can start Hyperion from your menu now" - -if [ $CPU_RPI -eq 1 ]; then - ENABLE_SERVICE=1 - STARTUP_MSG="echo ---> Hyperion has been installed as service, it will start on each system startup" -fi - start_msg="" restart_msg="" -if grep -m1 systemd /proc/1/comm > /dev/null -then - echo "---> init deamon: systemd" - # systemd - if [ $OS_HYPERBIAN -eq 1 ]; then - # start service only - echo "--> Service file already exists, skip creation" - start_msg="--> systemctl start hyperion" - systemctl start hyperion - else - install_file /usr/share/hyperion/service/hyperion.systemd /etc/systemd/system/hyperiond@.service - # service registration just on Raspberry Pi, probably need to ask the user how we should use the service. TODO service start in user login scope eg for x11?! - if [ $ENABLE_SERVICE -eq 1 ]; then - systemctl enable hyperiond"@${FOUND_USR}".service - start_msg="--> systemctl start hyperiond for user ${FOUND_USR}" - systemctl start hyperiond"@${FOUND_USR}" +# service registration if no gui is present (only on initial installation and not upgrade) +if [ "$IS_UPGRADE" = false ]; then + if [ -z "${DISPLAY}" ] && [ -z "${WAYLAND_DISPLAY}" ] && [ -z "${XDG_CURRENT_DESKTOP}" ]; then + STARTUP_MSG="echo ---> Hyperion has been installed as service, it will start on each system startup" + + if grep -m1 systemd /proc/1/comm > /dev/null + then + # systemd + echo "---> init deamon: systemd" + 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}" + systemctl start hyperion"@${FOUND_USR}" + elif [ -e /sbin/initctl ] + then + # upstart + echo "---> init deamon: upstart" + install_file /usr/share/hyperion/service/hyperion.initctl /etc/init/hyperion.conf && initctl reload-configuration + start_msg="--> initctl start hyperion" + initctl start hyperion + else + # sysV + echo "---> init deamon: sysV" + install_file /usr/share/hyperion/service/hyperion.init /etc/init.d/hyperion && chmod +x /etc/init.d/hyperion && update-rc.d hyperion defaults 98 02 + start_msg="---> service hyperion start" + service hyperion start fi - fi - -elif [ -e /sbin/initctl ] -then - echo "---> init deamon: upstart" - # upstart - if [ $ENABLE_SERVICE -eq 1 ]; then - install_file /usr/share/hyperion/service/hyperiond.initctl /etc/init/hyperion.conf && initctl reload-configuration - start_msg="--> initctl start hyperiond" - initctl start hyperiond - fi - -else - echo "---> init deamon: sysV" - # sysV - if [ $ENABLE_SERVICE -eq 1 ]; then - install_file /usr/share/hyperion/service/hyperion.init /etc/init.d/hyperiond && chmod +x /etc/init.d/hyperiond && update-rc.d hyperiond defaults 98 02 - start_msg="---> service hyperiond start" - service hyperiond start + else + STARTUP_MSG="echo ---> You can start Hyperion from your menu now" fi fi @@ -87,25 +78,30 @@ fi rm -r /usr/share/hyperion/service #link binarys and set exec bit -BINSP=/usr/share/hyperion/bin +BINSP=/usr/share/hyperion BINTP=/usr/bin -chmod +x -R $BINSP -ln -fs $BINSP/hyperiond $BINTP/hyperiond -ln -fs $BINSP/hyperion-remote $BINTP/hyperion-remote -ln -fs $BINSP/hyperion-v4l2 $BINTP/hyperion-v4l2 -ln -fs $BINSP/hyperion-framebuffer $BINTP/hyperion-framebuffer 2>/dev/null -ln -fs $BINSP/hyperion-dispmanx $BINTP/hyperion-dispmanx 2>/dev/null -ln -fs $BINSP/hyperion-x11 $BINTP/hyperion-x11 2>/dev/null -ln -fs $BINSP/hyperion-xcb $BINTP/hyperion-xcb 2>/dev/null -ln -fs $BINSP/hyperion-aml $BINTP/hyperion-aml 2>/dev/null -ln -fs $BINSP/hyperion-qt $BINTP/hyperion-qt 2>/dev/null +chmod +x -R $BINSP/bin +ln -fs $BINSP/bin/hyperiond $BINTP/hyperiond +ln -fs $BINSP/bin/hyperion-remote $BINTP/hyperion-remote +ln -fs $BINSP/bin/hyperion-v4l2 $BINTP/hyperion-v4l2 +ln -fs $BINSP/bin/hyperion-framebuffer $BINTP/hyperion-framebuffer 2>/dev/null +ln -fs $BINSP/bin/hyperion-dispmanx $BINTP/hyperion-dispmanx 2>/dev/null +ln -fs $BINSP/bin/hyperion-x11 $BINTP/hyperion-x11 2>/dev/null +ln -fs $BINSP/bin/hyperion-xcb $BINTP/hyperion-xcb 2>/dev/null +ln -fs $BINSP/bin/hyperion-aml $BINTP/hyperion-aml 2>/dev/null +ln -fs $BINSP/bin/hyperion-qt $BINTP/hyperion-qt 2>/dev/null -# install desktop icons / not on HyperBian -if [ $OS_HYPERBIAN -ne 1 ]; 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 +#create symlink for updateHyperionUser.sh script +ln -fs $BINSP/scripts/updateHyperionUser.sh $BINTP/updateHyperionUser 2>/dev/null + +# install desktop icons (only on initial installation and not upgrade) +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 + fi fi # cleanup desktop icons @@ -133,7 +129,11 @@ fi echo ${start_msg} echo "-----------------------------------------------------------------------------" -echo "---> Hyperion has been installed/updated!" +if [ "$IS_UPGRADE" = true ]; then + echo "---> Hyperion has been upgraded!" +else + echo "---> Hyperion has been installed!" +fi echo "---> " $STARTUP_MSG echo "---> For configuration, visit with your browser: ${NET_IP}:8090" diff --git a/cmake/package-scripts/preinst b/cmake/package-scripts/preinst new file mode 100644 index 00000000..3151540e --- /dev/null +++ b/cmake/package-scripts/preinst @@ -0,0 +1,33 @@ +#!/bin/sh + +echo "---Hyperion ambient light preinst ---" + +# search for users in system, returns first entry +FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" + +# stop running daemon before we install/upgrade +if pgrep hyperiond > /dev/null 2>&1 +then + if grep -m1 systemd /proc/1/comm > /dev/null + then + echo "--> stop init deamon: systemd" + # systemd + systemctl stop hyperion hyperiond"@${FOUND_USR}" hyperion"@${FOUND_USR}" 2> /dev/null + elif [ -e /sbin/initctl ] + then + echo "--> stop init deamon: upstart" + # upstart + initctl stop hyperiond 2>/dev/null + initctl stop hyperion 2>/dev/null + else + echo "--> stop init deamon: sysV" + # sysV + service hyperiond stop 2>/dev/null + service hyperion stop 2>/dev/null + fi +fi + +# In case we don't use a service kill all instances +killall hyperiond 2> /dev/null + +exit 0 diff --git a/cmake/debian/prerm b/cmake/package-scripts/prerm similarity index 71% rename from cmake/debian/prerm rename to cmake/package-scripts/prerm index f43561cf..d4e35627 100644 --- a/cmake/debian/prerm +++ b/cmake/package-scripts/prerm @@ -2,6 +2,17 @@ echo "---Hyperion ambient light prerm ---" +UPGRADE="$1" +if [ "$2" = "in-favour" ]; then + # Treat conflict remove as an upgrade. + UPGRADE="upgrade" +fi + +# Don't clean-up just for an upgrade.` +if [ "$action" = "upgrade" ] ; then + exit 0 +fi + # search for users in system, returns first entry FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" @@ -13,17 +24,18 @@ if grep -m1 systemd /proc/1/comm > /dev/null then echo "---> stop init deamon: systemd" # systemd - $HYPERION_RUNNING && systemctl stop hyperion hyperiond"@${FOUND_USR}" 2> /dev/null - # disable user specific symlink / not on HyperBian + $HYPERION_RUNNING && systemctl stop hyperion hyperiond"@${FOUND_USR}" hyperion"@${FOUND_USR}" 2> /dev/null + # disable user specific symlink echo "---> Disable service and remove entry" - systemctl -q disable hyperiond"@${FOUND_USR}" 2> /dev/null - rm -v /etc/systemd/system/hyperiond@.service 2>/dev/null + systemctl -q disable hyperion hyperiond"@${FOUND_USR}" hyperion"@${FOUND_USR}" 2> /dev/null + rm -v /etc/systemd/system/hyperion.service /etc/systemd/system/hyperiond@.service /etc/systemd/system/hyperion@.service 2>/dev/null elif [ -e /sbin/initctl ] then echo "---> stop init deamon: upstart" # upstart $HYPERION_RUNNING && initctl stop hyperiond + $HYPERION_RUNNING && initctl stop hyperion echo "---> Remove upstart service" rm -v /etc/init/hyperion* 2>/dev/null initctl reload-configuration @@ -32,6 +44,7 @@ else echo "---> stop init deamon: sysV" # sysV $HYPERION_RUNNING && service hyperiond stop 2>/dev/null + $HYPERION_RUNNING && service hyperion stop 2>/dev/null echo "---> Remove sysV service" update-rc.d -f hyperion remove rm /etc/init.d/hyperion* 2>/dev/null diff --git a/cmake/packages.cmake b/cmake/packages.cmake index 138f9d7c..cf1a4a63 100644 --- a/cmake/packages.cmake +++ b/cmake/packages.cmake @@ -65,7 +65,7 @@ ENDIF() # Specific CPack Package Generators # https://cmake.org/Wiki/CMake:CPackPackageGenerators # .deb files for apt -SET ( CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/preinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/postinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/prerm" ) +SET ( CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/preinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/postinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/prerm" ) SET ( CPACK_DEBIAN_PACKAGE_DEPENDS "libcec6 | libcec4" ) SET ( CPACK_DEBIAN_PACKAGE_SECTION "Miscellaneous" ) @@ -75,9 +75,9 @@ SET ( CPACK_RPM_PACKAGE_RELEASE 1 ) SET ( CPACK_RPM_PACKAGE_LICENSE "MIT" ) SET ( CPACK_RPM_PACKAGE_GROUP "Applications" ) SET ( CPACK_RPM_PACKAGE_REQUIRES "libcec >= 4.0.0" ) -SET ( CPACK_RPM_PRE_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/rpm/preinst" ) -SET ( CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/rpm/postinst" ) -SET ( CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/rpm/prerm" ) +SET ( CPACK_RPM_PRE_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/preinst" ) +SET ( CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/postinst" ) +SET ( CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/prerm" ) # OSX "Bundle" generator TODO Add more osx generators # https://cmake.org/cmake/help/v3.10/module/CPackBundle.html diff --git a/cmake/rpm/postinst b/cmake/rpm/postinst deleted file mode 100644 index 171b97b5..00000000 --- a/cmake/rpm/postinst +++ /dev/null @@ -1,148 +0,0 @@ -#!/bin/sh - -install_file() -{ - src="$1" - dest="$2" - - if [ ! -e "$dest" ] - then - cp "$src" "${dest}" - return 1 - else - echo "--> Service file already exists, skip creation" - return 0 - fi -} - - -echo "---Hyperion ambient light postinstall ---" - -#check system -CPU_RPI=`grep -m1 -c 'BCM2708\|BCM2709\|BCM2710\|BCM2835' /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` - -# search for users in system, returns first entry -FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" - -# determine if we should use a service -ENABLE_SERVICE=0 -STARTUP_MSG="echo ---> You can start Hyperion from your menu now" - -if [ $CPU_RPI -eq 1 ]; then - ENABLE_SERVICE=1 - STARTUP_MSG="echo ---> Hyperion has been installed as service, it will start on each system startup" -fi - -start_msg="" -restart_msg="" - -if grep -m1 systemd /proc/1/comm > /dev/null -then - echo "---> init deamon: systemd" - # systemd - install_file /usr/share/hyperion/service/hyperion.systemd /etc/systemd/system/hyperiond@.service - # service registration just on Raspberry Pi, probably need to ask the user how we should use the service. TODO service start in user login scope eg for x11?! - if [ $ENABLE_SERVICE -eq 1 ]; then - systemctl enable hyperiond"@${FOUND_USR}".service - start_msg="--> systemctl start hyperiond for user ${FOUND_USR}" - systemctl start hyperiond"@${FOUND_USR}" - fi - -elif [ -e /sbin/initctl ] -then - echo "---> init deamon: upstart" - # upstart - if [ $ENABLE_SERVICE -eq 1 ]; then - install_file /usr/share/hyperion/service/hyperiond.initctl /etc/init/hyperion.conf && initctl reload-configuration - start_msg="--> initctl start hyperiond" - initctl start hyperiond - fi - -else - echo "---> init deamon: sysV" - # sysV - if [ $ENABLE_SERVICE -eq 1 ]; then - install_file /usr/share/hyperion/service/hyperion.init /etc/init.d/hyperiond && chmod +x /etc/init.d/hyperiond && update-rc.d hyperiond defaults 98 02 - start_msg="---> service hyperiond start" - service hyperiond start - fi -fi - -#cleanup -rm -r /usr/share/hyperion/service - -#link binarys and set exec bit -BINSP=/usr/share/hyperion/bin -BINTP=/usr/bin -chmod +x -R $BINSP -ln -fs $BINSP/hyperiond $BINTP/hyperiond -ln -fs $BINSP/hyperion-remote $BINTP/hyperion-remote -ln -fs $BINSP/hyperion-v4l2 $BINTP/hyperion-v4l2 -ln -fs $BINSP/hyperion-framebuffer $BINTP/hyperion-framebuffer 2>/dev/null -ln -fs $BINSP/hyperion-dispmanx $BINTP/hyperion-dispmanx 2>/dev/null -ln -fs $BINSP/hyperion-x11 $BINTP/hyperion-x11 2>/dev/null -ln -fs $BINSP/hyperion-xcb $BINTP/hyperion-xcb 2>/dev/null -ln -fs $BINSP/hyperion-aml $BINTP/hyperion-aml 2>/dev/null -ln -fs $BINSP/hyperion-qt $BINTP/hyperion-qt 2>/dev/null - -# install desktop icons -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 - -# cleanup desktop icons -rm -r /usr/share/hyperion/desktop 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 "-----------------------------------------------------------------------------" -echo "---> Hyperion has been installed/updated!" -echo "---> " -$STARTUP_MSG -echo "---> For configuration, visit with your browser: ${NET_IP}:8090" -echo "---> or if already used by another service try: ${NET_IP}:8091" -$REBOOTMESSAGE -echo "-----------------------------------------------------------------------------" -echo "Webpage: www.hyperion-project.org" -echo "Forum: www.hyperion-project.org" -echo "Documenation: docs.hyperion-project.org" -echo "-----------------------------------------------------------------------------" - - -if [ -e /opt/hyperion/ ] -then - echo - echo "---------------------------------------------------------------------------------" - echo "- It seemd that you have an older version of hyperion installed in /opt/hyperion -" - echo "- please remove it to avoid problems -" - echo "---------------------------------------------------------------------------------" -fi - -exit 0 diff --git a/cmake/rpm/preinst b/cmake/rpm/preinst deleted file mode 100644 index e891e82e..00000000 --- a/cmake/rpm/preinst +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/sh - -echo "---Hyperion ambient light preinst ---" - -# search for users in system, returns first entry -FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" - -# stop running daemon before we install -if pgrep hyperiond > /dev/null 2>&1 -then - if grep -m1 systemd /proc/1/comm > /dev/null - then - echo "--> stop init deamon: systemd" - # systemd - systemctl stop hyperiond"@${FOUND_USR}" 2> /dev/null - - elif [ -e /sbin/initctl ] - then - echo "--> stop init deamon: upstart" - # upstart - initctl stop hyperiond - - else - echo "--> stop init deamon: sysV" - # sysV - service hyperiond stop 2>/dev/null - fi -fi - -# In case we don't use a service kill all instances -killall hyperiond 2> /dev/null - -exit 0 - - -#$USR=hyperionIS; - -#addToGroup() -##{ -# getent group $1 && adduser $USR $1; -#} - -#check if user exists -#if id $USR >/dev/null 2>&1; then -# echo "--> hyperion user exists, skip creation"; -#else - ## create user -# echo "--> Create Hyperion user"; -# adduser --system --group $USR; -#fi - -# add user to groups if required -## secondary user groups that are required to access system things -#addToGroup(dialout); -#addToGroup(video); -#addToGroup(audio); -#addToGroup(systemd-journal); -# platform specific groups -#addToGroup(i2c); -#addToGroup(spi); -#addToGroup(gpio); diff --git a/cmake/rpm/prerm b/cmake/rpm/prerm deleted file mode 100644 index ceb8c5a4..00000000 --- a/cmake/rpm/prerm +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh - -echo "---Hyperion ambient light prerm ---" - -# search for users in system, returns first entry -FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" - -# stop running daemon before we delete it -HYPERION_RUNNING=false -pgrep hyperiond > /dev/null 2>&1 && HYPERION_RUNNING=true - -if grep -m1 systemd /proc/1/comm > /dev/null -then - echo "---> stop init deamon: systemd" - # systemd - $HYPERION_RUNNING && systemctl stop hyperiond"@${FOUND_USR}" 2> /dev/null - # disable user specific symlink - echo "---> Disable service and remove entry" - systemctl -q disable hyperiond"@${FOUND_USR}" - rm -v /etc/systemd/system/hyperiond@.service 2>/dev/null - -elif [ -e /sbin/initctl ] -then - echo "---> stop init deamon: upstart" - # upstart - $HYPERION_RUNNING && initctl stop hyperiond - echo "---> Remove upstart service" - rm -v /etc/init/hyperion* 2>/dev/null - initctl reload-configuration - -else - echo "---> stop init deamon: sysV" - # sysV - $HYPERION_RUNNING && service hyperiond stop 2>/dev/null - echo "---> Remove sysV service" - update-rc.d -f hyperion remove - rm /etc/init.d/hyperion* 2>/dev/null -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 -rm -v /usr/share/applications/hyperion* 2>/dev/null -rm -rv /usr/share/pixmaps/hyperion 2>/dev/null - -exit 0 diff --git a/debian/rules b/debian/rules index c504276e..1c9608c8 100755 --- a/debian/rules +++ b/debian/rules @@ -3,7 +3,7 @@ export DH_VERBOSE = 1 BUILDDIR = build -build: +build: mkdir $(BUILDDIR); cd $(BUILDDIR); cmake -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../debian/tmp/usr .. make -j4 -C $(BUILDDIR) @@ -15,11 +15,11 @@ binary-indep: binary-arch: cd $(BUILDDIR); cmake -P cmake_install.cmake mkdir debian/tmp/DEBIAN - cp cmake/debian/postinst debian/tmp/DEBIAN + cp cmake/package-scripts/postinst debian/tmp/DEBIAN chmod 0775 debian/tmp/DEBIAN/postinst - cp cmake/debian/preinst debian/tmp/DEBIAN + cp cmake/package-scripts/preinst debian/tmp/DEBIAN chmod 0775 debian/tmp/DEBIAN/preinst - cp cmake/debian/prerm debian/tmp/DEBIAN + cp cmake/package-scripts/prerm debian/tmp/DEBIAN chmod 0775 debian/tmp/DEBIAN/prerm dpkg-gencontrol -phyperion dpkg --build debian/tmp .. diff --git a/dependencies/CMakeLists-mbedtls.txt.in b/dependencies/CMakeLists-mbedtls.txt.in index cb81adc8..d30bc08a 100644 --- a/dependencies/CMakeLists-mbedtls.txt.in +++ b/dependencies/CMakeLists-mbedtls.txt.in @@ -14,7 +14,7 @@ include(ExternalProject) ExternalProject_Add( mbedtls GIT_REPOSITORY "https://github.com/ARMmbed/mbedtls.git" - GIT_TAG "v2.25.0" # Reset to origin/master if issue https://github.com/ARMmbed/mbedtls/issues/4233 if fixed + GIT_TAG "v2.27.0" # Latest 2.x Version BUILD_ALWAYS OFF DOWNLOAD_DIR "${DOWNLOAD_DIR}" SOURCE_DIR "${SOURCE_DIR}" diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index f37aa83a..a59a6143 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -233,7 +233,7 @@ if (NOT USE_SYSTEM_MBEDTLS_LIBS) FetchContent_Declare( mbedtls GIT_REPOSITORY https://github.com/ARMmbed/mbedtls.git - GIT_TAG "v2.25.0" # Reset to origin/master if issue https://github.com/ARMmbed/mbedtls/issues/4233 if fixed + GIT_TAG "v2.27.0" # Latest 2.x Version BUILD_ALWAYS OFF GIT_PROGRESS 1 DOWNLOAD_DIR "${MBEDTLS_DOWNLOAD_DIR}" diff --git a/effects/fire.json b/effects/fire.json index c0109e3f..6a188f47 100644 --- a/effects/fire.json +++ b/effects/fire.json @@ -3,8 +3,14 @@ "script" : "gif.py", "args" : { - "image" : ":fire.gif", + "cropBottom": 0, + "cropLeft": 0, + "cropRight": 0, + "cropTop": 0, + "file" : ":fire.gif", "fps" :25, + "grayscale": false, + "imageSource": "file", "reverse" : false } } diff --git a/effects/gif.py b/effects/gif.py index c7ad4a7d..936f19b9 100644 --- a/effects/gif.py +++ b/effects/gif.py @@ -1,18 +1,27 @@ import hyperion, time # Get the parameters -imageFile = hyperion.args.get('image') +imageData = hyperion.args.get('url') if hyperion.args.get('imageSource', "") == "url" else hyperion.args.get('file') framesPerSecond = float(hyperion.args.get('fps', 25)) -reverse = bool(hyperion.args.get('reverse', False)) +reverse = bool(hyperion.args.get('reverse', False)) +cropLeft = int(hyperion.args.get('cropLeft', 0)) +cropTop = int(hyperion.args.get('cropTop', 0)) +cropRight = int(hyperion.args.get('cropRight', 0)) +cropBottom = int(hyperion.args.get('cropBottom', 0)) +grayscale = bool(hyperion.args.get('grayscale', False)) sleepTime = 1./framesPerSecond -imageList = [] +imageFrameList = [] -if imageFile: - imageList = [reversed(hyperion.getImage(imageFile))] if reverse else hyperion.getImage(imageFile) +if imageData: + if reverse: + imageFrameList = reversed(hyperion.getImage(imageData, cropLeft, cropTop, cropRight, cropBottom, grayscale)) + else: + imageFrameList = hyperion.getImage(imageData, cropLeft, cropTop, cropRight, cropBottom, grayscale) # Start the write data loop -while not hyperion.abort() and imageList: - for image in imageList: - hyperion.setImage(image["imageWidth"], image["imageHeight"], image["imageData"]) - time.sleep(sleepTime) +while not hyperion.abort() and imageFrameList: + for image in imageFrameList: + if not hyperion.abort(): + hyperion.setImage(image["imageWidth"], image["imageHeight"], image["imageData"]) + time.sleep(sleepTime) diff --git a/effects/lights.json b/effects/lights.json index 898e76b9..a68ba6e4 100644 --- a/effects/lights.json +++ b/effects/lights.json @@ -3,8 +3,14 @@ "script" : "gif.py", "args" : { - "image" : ":lights.gif", + "cropBottom": 0, + "cropLeft": 0, + "cropRight": 0, + "cropTop": 0, + "file" : ":lights.gif", "fps" :25, - "reverse" : false + "grayscale": false, + "imageSource": "file", + "reverse": false } } diff --git a/effects/matrix.json b/effects/matrix.json new file mode 100644 index 00000000..54403ae6 --- /dev/null +++ b/effects/matrix.json @@ -0,0 +1,16 @@ +{ + "name" : "Matrix", + "script" : "gif.py", + "args" : + { + "cropBottom": 0, + "cropLeft": 0, + "cropRight": 0, + "cropTop": 0, + "fps": 30, + "grayscale": false, + "imageSource": "url", + "reverse": false, + "url": "https://i.gifer.com/1j6F.gif" + } +} diff --git a/effects/schema/gif.schema.json b/effects/schema/gif.schema.json index 3161e3ac..e6165f20 100644 --- a/effects/schema/gif.schema.json +++ b/effects/schema/gif.schema.json @@ -4,17 +4,79 @@ "title":"edt_eff_gif_header", "required":true, "properties": { - "image": { + "imageSource": { + "type": "string", + "title": "edt_eff_image_source", + "format": "radio", + "enum": ["url", "file"], + "options" : { + "enum_titles" : ["edt_eff_image_source_url", "edt_eff_image_source_file"] + }, + "propertyOrder" : 1 + }, + "url": { + "type": "string", + "title":"edt_eff_url", + "default": "", + "options": { + "dependencies": { + "imageSource": "url" + } + }, + "propertyOrder" : 2 + }, + "file": { "type": "string", "title":"edt_eff_image", "format" : "url", "options" : { "upload" : true, - "auto_upload" : true + "auto_upload" : true, + "dependencies": { + "imageSource": "file" + } }, "default": "", - "propertyOrder" : 1 + "propertyOrder" : 3 + }, + "cropLeft": { + "type": "integer", + "title":"edt_conf_v4l2_cropLeft_title", + "default": 0, + "minimum" : 0, + "step" : 1, + "propertyOrder" : 4 + }, + "cropTop": { + "type": "integer", + "title":"edt_conf_v4l2_cropTop_title", + "default": 0, + "minimum" : 0, + "step" : 1, + "propertyOrder" : 5 + }, + "cropRight": { + "type": "integer", + "title":"edt_conf_v4l2_cropRight_title", + "default": 0, + "minimum" : 0, + "step" : 1, + "propertyOrder" : 6 + }, + "cropBottom": { + "type": "integer", + "title":"edt_conf_v4l2_cropBottom_title", + "default": 0, + "minimum" : 0, + "step" : 1, + "propertyOrder" : 7 + }, + "grayscale": { + "type": "boolean", + "title":"edt_eff_grayscale", + "default": false, + "propertyOrder" : 8 }, "fps": { "type": "integer", @@ -23,13 +85,13 @@ "minimum" : 1, "maximum" : 100, "step" : 1, - "propertyOrder" : 2 + "propertyOrder" : 9 }, "reverse": { "type": "boolean", "title":"edt_eff_reversedirection", "default": false, - "propertyOrder" : 3 + "propertyOrder" : 10 } }, "additionalProperties": false diff --git a/include/effectengine/EffectModule.h b/include/effectengine/EffectModule.h index d6a3f1b6..f76e7f62 100644 --- a/include/effectengine/EffectModule.h +++ b/include/effectengine/EffectModule.h @@ -8,8 +8,10 @@ class Effect; -class EffectModule +class EffectModule: public QObject { + Q_OBJECT + public: // Python 3 module def static struct PyModuleDef moduleDef; diff --git a/include/grabber/QtGrabber.h b/include/grabber/QtGrabber.h index 5eb65d5e..f04d8eca 100644 --- a/include/grabber/QtGrabber.h +++ b/include/grabber/QtGrabber.h @@ -79,6 +79,15 @@ public: /// bool open(); +#ifdef _WIN32 + /// + /// @brief Replacement for the virtual QWindowsScreen Function grabWindow (only on Windows). + /// + /// @return QPixmap + /// + QPixmap grabWindow(quintptr window, int xIn, int yIn, int width, int height) const; +#endif + private slots: /// diff --git a/libsrc/effectengine/EffectFileHandler.cpp b/libsrc/effectengine/EffectFileHandler.cpp index 6e4dc1df..f2862f62 100644 --- a/libsrc/effectengine/EffectFileHandler.cpp +++ b/libsrc/effectengine/EffectFileHandler.cpp @@ -70,9 +70,9 @@ QString EffectFileHandler::deleteEffect(const QString& effectName) { if (effectConfigurationFile.exists()) { - if ((it->script == ":/effects/gif.py") && !it->args.value("image").toString("").isEmpty()) + if ((it->script == ":/effects/gif.py") && !it->args.value("file").toString("").isEmpty()) { - QFileInfo effectImageFile(it->args.value("image").toString()); + QFileInfo effectImageFile(it->args.value("file").toString()); if (effectImageFile.exists()) { QFile::remove(effectImageFile.absoluteFilePath()); @@ -159,19 +159,26 @@ QString EffectFileHandler::saveEffect(const QJsonObject& message) newFileName.setFile(f); } - if (!message["imageData"].toString("").isEmpty() && !message["args"].toObject().value("image").toString("").isEmpty()) + if (!message["imageData"].toString("").isEmpty() && !message["args"].toObject().value("file").toString("").isEmpty()) { QJsonObject args = message["args"].toObject(); - QString imageFilePath = effectArray[0].toString().replace("$ROOT", _rootPath) + '/' + args.value("image").toString(); + QString imageFilePath = effectArray[0].toString().replace("$ROOT", _rootPath) + '/' + args.value("file").toString(); QFileInfo imageFileName(imageFilePath); if (!FileUtils::writeFile(imageFileName.absoluteFilePath(), QByteArray::fromBase64(message["imageData"].toString("").toUtf8()), _log)) { - return "Error while saving image file '" + message["args"].toObject().value("image").toString() + ", please check the Hyperion Log"; + return "Error while saving image file '" + message["args"].toObject().value("file").toString() + ", please check the Hyperion Log"; } //Update json with image file location - args["image"] = imageFilePath; + args["file"] = imageFilePath; + effectJson["args"] = args; + } + + if (message["args"].toObject().value("imageSource").toString("") == "url" || message["args"].toObject().value("imageSource").toString("") == "file") + { + QJsonObject args = message["args"].toObject(); + args.remove(args.value("imageSource").toString("") == "url" ? "file" : "url"); effectJson["args"] = args; } diff --git a/libsrc/effectengine/EffectModule.cpp b/libsrc/effectengine/EffectModule.cpp index a5e1f439..865df9b8 100644 --- a/libsrc/effectengine/EffectModule.cpp +++ b/libsrc/effectengine/EffectModule.cpp @@ -12,6 +12,10 @@ #include #include #include +#include +#include +#include +#include // Get the effect from the capsule #define getEffect() static_cast((Effect*)PyCapsule_Import("hyperion.__effectObj", 0)) @@ -219,31 +223,57 @@ PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args) PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args) { - QString file; QBuffer buffer; QImageReader reader; + char *source; + int cropLeft = 0, cropTop = 0, cropRight = 0, cropBottom = 0; + bool grayscale = false; if (getEffect()->_imageData.isEmpty()) { Q_INIT_RESOURCE(EffectEngine); - char *source; - if(!PyArg_ParseTuple(args, "s", &source)) + if(!PyArg_ParseTuple(args, "s|iiiii", &source, &cropLeft, &cropTop, &cropRight, &cropBottom, &grayscale)) { PyErr_SetString(PyExc_TypeError, "String required"); return nullptr; } - file = QString::fromUtf8(source); + const QUrl url = QUrl(source); + if (url.isValid()) + { + QNetworkAccessManager *networkManager = new QNetworkAccessManager(); + QNetworkReply * networkReply = networkManager->get(QNetworkRequest(url)); - if (file.mid(0, 1) == ":") - file = ":/effects/"+file.mid(1); + QEventLoop eventLoop; + connect(networkReply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit); + eventLoop.exec(); - reader.setDecideFormatFromContent(true); - reader.setFileName(file); + if (networkReply->error() == QNetworkReply::NoError) + { + buffer.setData(networkReply->readAll()); + buffer.open(QBuffer::ReadOnly); + reader.setDecideFormatFromContent(true); + reader.setDevice(&buffer); + } + + delete networkReply; + delete networkManager; + } + else + { + QString file = QString::fromUtf8(source); + + if (file.mid(0, 1) == ":") + file = ":/effects/"+file.mid(1); + + reader.setDecideFormatFromContent(true); + reader.setFileName(file); + } } else { + PyArg_ParseTuple(args, "|siiiii", &source, &cropLeft, &cropTop, &cropRight, &cropBottom, &grayscale); buffer.setData(QByteArray::fromBase64(getEffect()->_imageData.toUtf8())); buffer.open(QBuffer::ReadOnly); reader.setDecideFormatFromContent(true); @@ -260,19 +290,33 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args) if (reader.canRead()) { QImage qimage = reader.read(); - int width = qimage.width(); int height = qimage.height(); + if (cropLeft > 0 || cropTop > 0 || cropRight > 0 || cropBottom > 0) + { + if (cropLeft + cropRight >= width || cropTop + cropBottom >= height) + { + QString errorStr = QString("Rejecting invalid crop values: left: %1, right: %2, top: %3, bottom: %4, higher than height/width %5/%6").arg(cropLeft).arg(cropRight).arg(cropTop).arg(cropBottom).arg(height).arg(width); + PyErr_SetString(PyExc_RuntimeError, qPrintable(errorStr)); + return nullptr; + } + + qimage = qimage.copy(cropLeft, cropTop, width - cropLeft - cropRight, height - cropTop - cropBottom); + width = qimage.width(); + height = qimage.height(); + } + QByteArray binaryImage; - for (int i = 0; i(qimage.scanLine(i)); - for (int j = 0; j< width; ++j) + const QRgb *end = scanline + qimage.width(); + for (; scanline != end; scanline++) { - binaryImage.append((char) qRed(scanline[j])); - binaryImage.append((char) qGreen(scanline[j])); - binaryImage.append((char) qBlue(scanline[j])); + binaryImage.append(!grayscale ? (char) qRed(scanline[0]) : (char) qGray(scanline[0])); + binaryImage.append(!grayscale ? (char) qGreen(scanline[1]) : (char) qGray(scanline[1])); + binaryImage.append(!grayscale ? (char) qBlue(scanline[2]) : (char) qGray(scanline[2])); } } PyList_SET_ITEM(result, i, Py_BuildValue("{s:i,s:i,s:O}", "imageWidth", width, "imageHeight", height, "imageData", PyByteArray_FromStringAndSize(binaryImage.constData(),binaryImage.size()))); @@ -283,6 +327,7 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args) return nullptr; } } + return result; } else diff --git a/libsrc/grabber/qt/QtGrabber.cpp b/libsrc/grabber/qt/QtGrabber.cpp index 7ee698ef..924825a0 100644 --- a/libsrc/grabber/qt/QtGrabber.cpp +++ b/libsrc/grabber/qt/QtGrabber.cpp @@ -11,6 +11,10 @@ #include #include +#ifdef _WIN32 +#include +#endif + // Constants namespace { const bool verbose = false; @@ -156,6 +160,58 @@ void QtGrabber::geometryChanged(const QRect &geo) updateScreenDimensions(true); } +#ifdef _WIN32 +extern QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int format = 0); + +QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int height) const +{ + QSize windowSize; + int x = xIn; + int y = yIn; + HWND hwnd = reinterpret_cast(window); + if (hwnd) + { + RECT r; + GetClientRect(hwnd, &r); + windowSize = QSize(r.right - r.left, r.bottom - r.top); + } + else + { + hwnd = GetDesktopWindow(); + const QRect screenGeometry = _screen->geometry(); + windowSize = screenGeometry.size(); + x += screenGeometry.x(); + y += screenGeometry.y(); + } + + if (width < 0) + width = windowSize.width() - x; + + if (height < 0) + height = windowSize.height() - y; + + // Create and setup bitmap + HDC display_dc = GetDC(nullptr); + HDC bitmap_dc = CreateCompatibleDC(display_dc); + HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height); + HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap); + + // copy data + HDC window_dc = GetDC(hwnd); + BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY); + + // clean up all but bitmap + ReleaseDC(hwnd, window_dc); + SelectObject(bitmap_dc, null_bitmap); + DeleteDC(bitmap_dc); + const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap); + DeleteObject(bitmap); + ReleaseDC(nullptr, display_dc); + + return pixmap; +} +#endif + int QtGrabber::grabFrame(Image & image) { int rc = 0; @@ -170,7 +226,11 @@ int QtGrabber::grabFrame(Image & image) if (_isEnabled) { +#ifdef _WIN32 + 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 if (originalPixmap.isNull()) { rc = -1; diff --git a/libsrc/python/PythonInit.cpp b/libsrc/python/PythonInit.cpp index bcac8d9c..497f6ae7 100644 --- a/libsrc/python/PythonInit.cpp +++ b/libsrc/python/PythonInit.cpp @@ -23,17 +23,26 @@ PythonInit::PythonInit() // register modules EffectModule::registerHyperionExtensionModule(); + // Set Program name + Py_SetProgramName(L"Hyperion"); + // set Python module path when exists - QString py_patch = QDir::cleanPath(qApp->applicationDirPath() + "/../lib/python"); + QString py_patch = QDir::cleanPath(qApp->applicationDirPath() + "/../lib/python" + STRINGIFY(PYTHON_VERSION_MAJOR_MINOR)); QString py_file = QDir::cleanPath(qApp->applicationDirPath() + "/python" + STRINGIFY(PYTHON_VERSION_MAJOR_MINOR) + ".zip"); if (QFile(py_file).exists() || QDir(py_patch).exists()) { Py_NoSiteFlag++; if (QFile(py_file).exists()) + { + Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr)); Py_SetPath(Py_DecodeLocale(py_file.toLatin1().data(), nullptr)); + } else if (QDir(py_patch).exists()) + { + Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr)); Py_SetPath(Py_DecodeLocale(py_patch.toLatin1().data(), nullptr)); + } } // init Python diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index e9aa9aa5..74e50486 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -138,6 +138,7 @@ if(CMAKE_HOST_UNIX) install( CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/hyperiond\" \"${CMAKE_BINARY_DIR}/symlink_hyperiond\" )" COMPONENT "Hyperion" ) install( FILES ${CMAKE_BINARY_DIR}/symlink_hyperiond DESTINATION "bin" RENAME hyperiond COMPONENT "Hyperion" ) install( CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_hyperiond )" COMPONENT "Hyperion" ) + install ( FILES ${CMAKE_SOURCE_DIR}/bin/scripts/updateHyperionUser.sh DESTINATION "share/hyperion/scripts" COMPONENT "Hyperion" ) endif() # Deploy Qt DLLs into the binary folder.