mirror of
				https://github.com/hyperion-project/hyperion.ng.git
				synced 2025-03-01 10:33:28 +00:00 
			
		
		
		
	Commits from @MartB and more ...
- Commit: 1d9165f403
- New default QT capture implementation
- UploadHandler added to Effects Configurator to allow uploading GIF files
- Docker compile script and instruction
- Travis Fix
			
			
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -4,4 +4,4 @@ | ||||
| 	branch = master | ||||
| [submodule "dependencies/external/flatbuffers"] | ||||
| 	path = dependencies/external/flatbuffers | ||||
| 	url = git://github.com/google/flatbuffers.git | ||||
| 	url = https://github.com/google/flatbuffers | ||||
|   | ||||
							
								
								
									
										12
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -5,11 +5,20 @@ cache: | ||||
| notifications: | ||||
|     email: false | ||||
| language: cpp | ||||
| services: | ||||
|     - docker | ||||
| matrix: | ||||
|   include: | ||||
|     - os: linux | ||||
|       dist: trusty | ||||
|       sudo: required | ||||
|       env: | ||||
|           - DOCKER_TAG=ubuntu1604 | ||||
|           - DOCKER_NAME="Ubuntu 16.04" | ||||
|     - os: linux | ||||
|       dist: trusty | ||||
|       env: | ||||
|           - DOCKER_TAG=cross-qemu-rpistretch | ||||
|           - DOCKER_NAME="Raspberry Pi" | ||||
|     - os: osx | ||||
|       osx_image: xcode7.3 | ||||
|       env: | ||||
| @@ -18,6 +27,5 @@ before_install: | ||||
|     - ./.travis/travis_install.sh | ||||
| script: | ||||
|     - ./.travis/travis_build.sh | ||||
|     - ./test/testrunner.sh | ||||
| after_success: | ||||
|     - ./.travis/travis_deploy.sh | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
|  | ||||
| PLATFORM=x86 | ||||
| BUILD_TYPE=Debug | ||||
| PACKAGES="" | ||||
|  | ||||
| # Detect number of processor cores | ||||
| # default is 4 jobs | ||||
| @@ -16,32 +17,48 @@ elif [[ "$TRAVIS_OS_NAME" == 'linux' ]] | ||||
| then | ||||
| 	JOBS=$(nproc) | ||||
| fi | ||||
| echo "compile jobs: ${JOBS:=4}" | ||||
|  | ||||
| # compile prepare | ||||
| mkdir build || exit 1 | ||||
| cd build | ||||
|  | ||||
| # Compile hyperion for tags | ||||
| # Determine cmake build type; tag builds are Release, else Debug | ||||
| [ -n "${TRAVIS_TAG:-}" ] && BUILD_TYPE=Release | ||||
|  | ||||
| # Compile hyperion for cron - take default settings | ||||
| # Determine package creation; True for cron and tag builds | ||||
| [ "${TRAVIS_EVENT_TYPE:-}" == 'cron' ] || [ -n "${TRAVIS_TAG:-}" ] && PACKAGES=package | ||||
|  | ||||
| # Compile for PR (no tag and no cron) | ||||
| # Determie -dev appends to platform; | ||||
| [ "${TRAVIS_EVENT_TYPE:-}" != 'cron' -a -z "${TRAVIS_TAG:-}" ] && PLATFORM=${PLATFORM}-dev | ||||
|  | ||||
| cmake -DPLATFORM=$PLATFORM -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_INSTALL_PREFIX=/usr .. || exit 2 | ||||
| if [[ "$TRAVIS_OS_NAME" == 'linux' ]] | ||||
| # Build the package on osx | ||||
| if [[ "$TRAVIS_OS_NAME" == 'osx' || "$TRAVIS_OS_NAME" == 'darwin' ]] | ||||
| then | ||||
| 	# activate dispmanx and osx mocks | ||||
| 	cmake -DENABLE_OSX=ON -DENABLE_DISPMANX=ON .. || exit 5 | ||||
| 	# compile prepare | ||||
| 	mkdir build || exit 1 | ||||
| 	cd build | ||||
| 	cmake -DPLATFORM=$PLATFORM -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_INSTALL_PREFIX=/usr .. || exit 2 | ||||
| 	make -j ${JOBS} || exit 3 | ||||
| fi | ||||
|  | ||||
| echo "compile jobs: ${JOBS:=4}" | ||||
| make -j ${JOBS} || exit 3 | ||||
|  | ||||
| # Build the package on Linux | ||||
| # Build the package with docker | ||||
| if [[ $TRAVIS_OS_NAME == 'linux' ]] | ||||
| then | ||||
| 	make -j ${JOBS} package || exit 4 | ||||
| fi | ||||
| 	echo "Compile Hyperion with DOCKER_TAG = ${DOCKER_TAG} and friendly name DOCKER_NAME = ${DOCKER_NAME}" | ||||
| 	# take ownership of deploy dir | ||||
| 	mkdir $TRAVIS_BUILD_DIR/deploy | ||||
| 	# run docker | ||||
| 	docker run --rm \ | ||||
| 		-v "${TRAVIS_BUILD_DIR}/deploy:/deploy" \ | ||||
| 		-v "${TRAVIS_BUILD_DIR}:/source:ro" \ | ||||
| 		hyperionorg/hyperion-ci:$DOCKER_TAG \ | ||||
| 		/bin/bash -c "mkdir build && cp -r /source/. /build && | ||||
| 		cd /build && mkdir build && cd build && | ||||
| 		cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. || exit 2 && | ||||
| 		make -j $(nproc) ${PACKAGES} || exit 3 && | ||||
| 		echo '---> Copy binaries and packages to host folder: ${TRAVIS_BUILD_DIR}/deploy' && | ||||
| 		cp -v /build/build/bin/h* /deploy/ 2>/dev/null || : && | ||||
| 		cp -v /build/build/Hyperion-* /deploy/ 2>/dev/null || : && | ||||
| 		exit 0; | ||||
| 		exit 1 " || { echo "---> Hyperion compilation failed! Abort"; exit 4; } | ||||
|  | ||||
| 	# overwrite file owner to current user | ||||
| 	sudo chown -fR $(stat -c "%U:%G" $TRAVIS_BUILD_DIR/deploy) $TRAVIS_BUILD_DIR/deploy | ||||
| fi | ||||
|   | ||||
| @@ -1,10 +1,13 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # sf_upload <deploylist> <sf_dir> | ||||
| # sf_upload <FILES> <sf_dir> | ||||
| sf_upload() | ||||
| { | ||||
| echo "Uploading following files: ${1} | ||||
| to dir /hyperion-project/${2}" | ||||
|  | ||||
| 	/usr/bin/expect <<-EOD | ||||
| 	spawn scp $1 hyperionsf37@frs.sourceforge.net:/home/frs/project/hyperion-project/dev/$2 | ||||
| 	spawn scp $1hyperionsf37@frs.sourceforge.net:/home/frs/project/hyperion-project/$2 | ||||
| 	expect "*(yes/no)*" | ||||
| 	send "yes\r" | ||||
| 	expect "*password:*" | ||||
| @@ -13,18 +16,49 @@ sf_upload() | ||||
| 	EOD | ||||
| } | ||||
|  | ||||
| deploylist="hyperion-2.0.0-Linux-x86.tar.gz" | ||||
| # append current Date to filename (just packages no binaries) | ||||
| appendDate() | ||||
| { | ||||
| 	D=$(date +%Y-%m-%d) | ||||
| 	for F in $TRAVIS_BUILD_DIR/deploy/Hy* | ||||
| 	do | ||||
| 		mv "$F" "${F%.*}-$D.${F##*.}" | ||||
| 	done | ||||
| } | ||||
|  | ||||
| # append friendly name (just packages no binaries) | ||||
| appendName() | ||||
| { | ||||
| 	for F in $TRAVIS_BUILD_DIR/deploy/Hy* | ||||
| 	do | ||||
| 		mv "$F" "${F%.*}-($DOCKER_NAME).${F##*.}" | ||||
| 	done | ||||
| } | ||||
|  | ||||
| # get all files to deploy (just packages no binaries) | ||||
| getFiles() | ||||
| { | ||||
| 	FILES="" | ||||
| 	for f in $TRAVIS_BUILD_DIR/deploy/Hy*; | ||||
| 		do FILES+="${f} "; | ||||
| 	done; | ||||
| } | ||||
|  | ||||
| if [[ $TRAVIS_OS_NAME == 'linux' ]]; then | ||||
| 	cd $TRAVIS_BUILD_DIR/build | ||||
| 	if [[ -n $TRAVIS_TAG ]]; then | ||||
| 		echo "tag upload" | ||||
| 		sf_upload $deploylist release | ||||
| 		appendName | ||||
| 		appendDate | ||||
| 		getFiles | ||||
| 		sf_upload $FILES release | ||||
| 	elif [[ $TRAVIS_EVENT_TYPE == 'cron' ]]; then | ||||
| 		echo "cron upload" | ||||
| 		sf_upload $deploylist alpha | ||||
| 		appendName | ||||
| 		appendDate | ||||
| 		getFiles | ||||
| 		sf_upload $FILES dev/alpha | ||||
| 	else | ||||
| 		echo "PR can't be uploaded for security reasons" | ||||
| 		sf_upload $deploylist pr | ||||
| 		echo "Direct pushed no upload, PRs not possible" | ||||
| 		#sf_upload $FILES pr | ||||
| 	fi | ||||
| fi | ||||
|   | ||||
| @@ -18,8 +18,8 @@ then | ||||
| elif [[ $TRAVIS_OS_NAME == 'linux' ]] | ||||
| then | ||||
| 	echo "Install linux deps" | ||||
| 	sudo apt-get -qq update | ||||
| 	sudo apt-get install -qq -y qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev doxygen expect | ||||
| 	#sudo apt-get -qq update | ||||
| 	#sudo apt-get install -qq -y qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev doxygen expect | ||||
| else | ||||
|     echo "Unsupported platform: $TRAVIS_OS_NAME" | ||||
|     exit 5 | ||||
|   | ||||
| @@ -20,6 +20,7 @@ SET ( DEFAULT_AMLOGIC    OFF ) | ||||
| SET ( DEFAULT_DISPMANX   OFF ) | ||||
| SET ( DEFAULT_OSX        OFF ) | ||||
| SET ( DEFAULT_X11        OFF ) | ||||
| SET ( DEFAULT_QT         ON ) | ||||
| SET ( DEFAULT_WS281XPWM  OFF ) | ||||
| SET ( DEFAULT_USE_SHARED_AVAHI_LIBS  ON ) | ||||
| SET ( DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS OFF ) | ||||
| @@ -151,7 +152,8 @@ message(STATUS "ENABLE_USB_HID = ${ENABLE_USB_HID}") | ||||
| option(ENABLE_X11 "Enable the X11 grabber" ${DEFAULT_X11}) | ||||
| message(STATUS "ENABLE_X11 = ${ENABLE_X11}") | ||||
|  | ||||
| SET(ENABLE_QT5 ON) | ||||
| option(ENABLE_QT "Enable the qt grabber" ${DEFAULT_QT}) | ||||
| message(STATUS "ENABLE_QT = ${ENABLE_QT}") | ||||
|  | ||||
| option(ENABLE_TESTS "Compile additional test applications" ${DEFAULT_TESTS}) | ||||
| message(STATUS "ENABLE_TESTS = ${ENABLE_TESTS}") | ||||
| @@ -159,7 +161,6 @@ message(STATUS "ENABLE_TESTS = ${ENABLE_TESTS}") | ||||
| option(ENABLE_PROFILER "enable profiler capabilities - not for release code" OFF) | ||||
| message(STATUS "ENABLE_PROFILER = ${ENABLE_PROFILER}") | ||||
|  | ||||
|  | ||||
| SET ( FLATBUFFERS_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/flatbuf ) | ||||
| SET ( FLATBUFFERS_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/flatbuf ) | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,18 @@ | ||||
| # Install the required tools and dependencies | ||||
| # 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 compilation for Raspberry Pi (Raspbian stretch) | ||||
|  | ||||
| To compile Hyperion for Ubuntu 16.04 (x64) or higher just execute the following command | ||||
| ``` | ||||
| wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh | ||||
| ``` | ||||
| To compile Hyperion for Raspberry Pi | ||||
| ``` | ||||
| wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh -t cross-qemu-rpistretch | ||||
| ``` | ||||
| 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 | ||||
|  | ||||
| # The usual way | ||||
|  | ||||
| ## Debian/Ubuntu/Win10LinuxSubsystem | ||||
|  | ||||
| @@ -60,12 +74,12 @@ sudo make install/strip | ||||
| sudo make uninstall | ||||
| # ... or run it from compile directory | ||||
| bin/hyperiond | ||||
| # webui is located on localhost:8099 | ||||
| # webui is located on localhost:8090 or 8091 | ||||
| ``` | ||||
|  | ||||
|  | ||||
| ### Download | ||||
|  Create hyperion directory and checkout the code from github | ||||
|  Creates hyperion directory and checkout the code from github | ||||
|  | ||||
| You might want to add `--depth 1` to the `git` command if you only want to compile the current source and have no need for the entire git repository | ||||
|  | ||||
| @@ -74,7 +88,7 @@ export HYPERION_DIR="hyperion" | ||||
| git clone --recursive https://github.com/hyperion-project/hyperion.ng.git "$HYPERION_DIR" | ||||
| ``` | ||||
|  | ||||
| **Note:** If you forget the --recursive in above statement or you are updating an existing clone you need to clone the protobuf submodule by runnning the follwing two statements: | ||||
| **Note:** If you forget the --recursive in above statement or you are updating an existing clone you need to clone the flatbuffers submodule by runnning the follwing two statements: | ||||
| ``` | ||||
| git submodule init | ||||
| git submodule update | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| sudo apt-get update | ||||
| sudo apt-get upgrade | ||||
| #TO-DO verify what is really required | ||||
| #blacklist: protobuf-compiler lib32z1 lib32ncurses5 lib32bz2-1.0 zlib1g-dev | ||||
| #blacklist: lib32z1 lib32ncurses5 lib32bz2-1.0 zlib1g-dev | ||||
| sudo apt-get -qq -y install git rsync cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev | ||||
|  | ||||
| echo 'PATH=$PATH:$HOME/raspberrypi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin' >> .bashrc | ||||
| @@ -58,12 +58,12 @@ git clone --depth 1 git://github.com/raspberrypi/tools.git "$RASCROSS_DIR/tools" | ||||
| # get the Hyperion sources | ||||
| git clone --recursive https://github.com/hyperion-project/hyperion.ng.git "$HYPERION_DIR" | ||||
|  | ||||
| # do a native build (to build the protobuf compiler for the native platform) | ||||
| # do a native build (to build the flatbuffers compiler for the native platform) | ||||
| mkdir -p "$NATIVE_BUILD_DIR" | ||||
| cmake -DENABLE_DISPMANX=OFF --build "$NATIVE_BUILD_DIR" "$HYPERION_DIR" | ||||
|  | ||||
| # do the cross build  | ||||
| # specify the protoc export file to import the protobuf compiler from the native build | ||||
| # specify the protoc export file to import the flatbuffers compiler from the native build | ||||
| mkdir -p "$TARGET_BUILD_DIR" | ||||
| cmake -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" -DIMPORT_PROTOC=$NATIVE_BUILD_DIR/protoc_export.cmake --build "$TARGET_BUILD_DIR" "$HYPERION_DIR" | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,9 @@ | ||||
| // Define to enable the x11 grabber | ||||
| #cmakedefine ENABLE_X11 | ||||
|  | ||||
| // Define to enable the qt grabber | ||||
| #cmakedefine ENABLE_QT | ||||
|  | ||||
| // Define to enable the spi-device | ||||
| #cmakedefine ENABLE_SPIDEV | ||||
|  | ||||
| @@ -42,5 +45,3 @@ | ||||
| #define HYPERION_VERSION       "${HYPERION_VERSION_MAJOR}.${HYPERION_VERSION_MINOR}.${HYPERION_VERSION_PATCH}" | ||||
|  | ||||
| #define HYPERION_JSON_VERSION "1.0.0" | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -285,6 +285,7 @@ | ||||
| 	"InfoDialog_nowrite_text" : "Hyperion hat keinen Schreibzugriff auf die aktuell geladene Konfiguration. Bitte korrigiere die Dateizugriffsrechte um fortzufahren.", | ||||
| 	"InfoDialog_nowrite_foottext" : "Die Webkonfiguration wird automatisch wieder freigegeben, sobald das Problem behoben wurde!", | ||||
| 	"infoDialog_wizrgb_text" : "Deine RGB Byte Reihenfolge ist bereits richtig eingestellt.", | ||||
| 	"infoDialog_writeimage_error_text": "Die ausgewählte Datei \"$1\" ist keine Bilddatei oder ist beschädigt! Bitte wähle eine andere Bilddatei aus.", | ||||
| 	"infoDialog_writeconf_error_text" : "Das speichern der Konfiguration ist fehlgeschlagen.", | ||||
| 	"infoDialog_import_jsonerror_text" : "Die ausgewählte Konfigurations-Datei \"$1\" ist keine .json Datei oder ist beschädigt! Fehlermeldung: ($2)", | ||||
| 	"infoDialog_import_hyperror_text" : "Die ausgewählte Konfigurations-Datei \"$1\" kann nicht importiert werden. Sie ist nicht kompatibel mit Hyperion 2.0 und höher!", | ||||
|   | ||||
| @@ -285,6 +285,7 @@ | ||||
| 	"InfoDialog_nowrite_text" : "Hyperion can't write to your current loaded configuration file. Please repair the file permissions to proceed.", | ||||
| 	"InfoDialog_nowrite_foottext" : "The WebUI will be unlocked automatically after you solved the problem!", | ||||
| 	"infoDialog_wizrgb_text" : "Your RGB Byte Order is already well adjusted.", | ||||
| 	"infoDialog_writeimage_error_text": "The selected file \"$1\" is no image file or it's corrupted! Please select another image file.", | ||||
| 	"infoDialog_writeconf_error_text" : "Saving your configuration failed.", | ||||
| 	"infoDialog_import_jsonerror_text" : "The selected configuration file \"$1\" is no .json file or it's corrupted. Error message: ($2)", | ||||
| 	"infoDialog_import_hyperror_text" : "The selected configuration file \"$1\" can't be imported. It's not compatible with Hyperion 2.0 and higher!", | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| { | ||||
|     "@metadata": { | ||||
|         "authors": [ | ||||
|             "brindosch" | ||||
|             "brindosch, paulchen-panther" | ||||
|         ], | ||||
| 		"project" : "Hyperion WebUI string docu", | ||||
| 		"last-updated": "2016-11-30" | ||||
| 		"last-updated": "2019-01-02" | ||||
|     }, | ||||
| 	"edt_msg_error_notset" : " When a property is not set", | ||||
| 	"edt_msg_error_notempty" : "When a string must not be empty", | ||||
| @@ -40,8 +40,14 @@ | ||||
| 	"edt_msg_button_add_row_title" : "Title on Add Row buttons. $1 = This key takes one variable: The title of object to add", | ||||
| 	"edt_msg_button_move_down_title" : "Title on Move Down buttons", | ||||
| 	"edt_msg_button_move_up_title" : "Title on Move Up buttons", | ||||
| 	"edt_msg_button_delete_row_titlet" : "Title on Delete Row buttons. $1 = This key takes one variable: The title of object to delete", | ||||
| 	"edt_msg_button_delete_row_title" : "Title on Delete Row buttons. $1 = This key takes one variable: The title of object to delete", | ||||
| 	"edt_msg_button_delete_row_title_short" : "Title on Delete Row buttons, short version (no parameter with the object title)", | ||||
| 	"edt_msg_button_collapse" : "Title on Collapse buttons", | ||||
| 	"edt_msg_button_expand" : "Title on Expand buttons" | ||||
| } | ||||
| 	"edt_msg_button_expand" : "Title on Expand buttons", | ||||
| 	"edt_msg_error_date" : "When a date is in incorrect format. $1 = This key takes one variable: The valid format", | ||||
| 	"edt_msg_error_time" : "When a time is in incorrect format. $1 = This key takes one variable: The valid format", | ||||
| 	"edt_msg_error_datetime_local" : "When a datetime-local is in incorrect format. $1 = This key takes one variable: The valid format", | ||||
| 	"edt_msg_error_invalid_epoch" : "When a integer date is less than 1 January 1970", | ||||
| 	"edt_msg_flatpickr_toggle_button" : "Title on Flatpickr toggle buttons", | ||||
| 	"edt_msg_flatpickr_clear_button" : "Title on Flatpickr clear buttons" | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ $(document).ready( function() { | ||||
| 	performTranslation(); | ||||
| 	var oldDelList = []; | ||||
| 	var effectName = ""; | ||||
| 	var imageData = ""; | ||||
| 	var effects_editor = null; | ||||
| 	var effectPy = ""; | ||||
| 	var testrun; | ||||
| @@ -31,9 +32,30 @@ $(document).ready( function() { | ||||
| 	function triggerTestEffect() { | ||||
| 		testrun = true; | ||||
| 		var args = effects_editor.getEditor('root.args'); | ||||
| 		requestTestEffect(effectName, ":/effects/" + effectPy.slice(1), JSON.stringify(args.getValue())); | ||||
| 		requestTestEffect(effectName, ":/effects/" + effectPy.slice(1), JSON.stringify(args.getValue()), imageData); | ||||
| 	}; | ||||
|  | ||||
| 	// Specify upload handler for image files | ||||
| 	JSONEditor.defaults.options.upload = function(type, file, cbs) { | ||||
| 		var fileReader = new FileReader(); | ||||
|  | ||||
| 		//check file | ||||
| 		if (!file.type.startsWith('image')) { | ||||
| 			imageData = ""; | ||||
| 			cbs.failure('File upload error'); | ||||
| 			// TODO clear file dialog. | ||||
| 			showInfoDialog('error', "", $.i18n('infoDialog_writeimage_error_text', file.name)); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		fileReader.onload = function () { | ||||
| 			imageData = this.result.split(',')[1]; | ||||
| 			console.log(imageData); | ||||
| 			cbs.success(file.name); | ||||
| 		}; | ||||
|  | ||||
| 		fileReader.readAsDataURL(file); | ||||
| 	}; | ||||
|  | ||||
| 	$("#effectslist").off().on("change", function(event) { | ||||
| 		if(effects_editor != null) | ||||
| @@ -48,6 +70,7 @@ $(document).ready( function() { | ||||
|  | ||||
| 				effectPy = ':'; | ||||
| 				effectPy += effects[idx].schemaContent.script; | ||||
| 				imageData = ""; | ||||
| 				$("#name-input").trigger("change"); | ||||
|  | ||||
| 				$("#eff_desc").html(createEffHint($.i18n(effects[idx].schemaContent.title),$.i18n(effects[idx].schemaContent.title+'_desc'))); | ||||
| @@ -84,7 +107,7 @@ $(document).ready( function() { | ||||
|  | ||||
| 	// Save Effect | ||||
| 	$('#btn_write').off().on('click',function() { | ||||
| 		requestWriteEffect(effectName,effectPy,JSON.stringify(effects_editor.getValue())); | ||||
| 		requestWriteEffect(effectName,effectPy,JSON.stringify(effects_editor.getValue()),imageData); | ||||
| 		$(hyperion).one("cmd-create-effect", function(event) { | ||||
| 			if (event.response.success) | ||||
| 				showInfoDialog('success', "", $.i18n('infoDialog_effconf_created_text', effectName)); | ||||
| @@ -163,9 +186,9 @@ $(document).ready( function() { | ||||
| 	//create basic effect list | ||||
| 	var effects = serverSchema.properties.effectSchemas.internal | ||||
| 	for(var idx=0; idx<effects.length; idx++) | ||||
| 		{ | ||||
| 			$("#effectslist").append(createSelOpt(effects[idx].schemaContent.script, $.i18n(effects[idx].schemaContent.title))); | ||||
| 		} | ||||
| 	{ | ||||
| 		$("#effectslist").append(createSelOpt(effects[idx].schemaContent.script, $.i18n(effects[idx].schemaContent.title))); | ||||
| 	} | ||||
| 	$("#effectslist").trigger("change"); | ||||
|  | ||||
| 	updateDelEffectlist(); | ||||
|   | ||||
| @@ -41,7 +41,7 @@ $(document).ready( function() { | ||||
| 		currentVersion = sysInfo.hyperion.version; | ||||
| 	}); | ||||
|  | ||||
| 	$(hyperion).on("cmd-config-getschema", function(event) { | ||||
| 	$(hyperion).one("cmd-config-getschema", function(event) { | ||||
| 		serverSchema = event.response.info; | ||||
| 		requestServerConfig(); | ||||
|  | ||||
| @@ -63,7 +63,7 @@ $(document).ready( function() { | ||||
| 		requestServerConfigSchema(); | ||||
| 	}); | ||||
|  | ||||
| 	$(hyperion).on("ready", function(event) { | ||||
| 	$(hyperion).one("ready", function(event) { | ||||
| 		loadContent(); | ||||
| 	}); | ||||
|  | ||||
|   | ||||
| @@ -270,15 +270,15 @@ function requestWriteConfig(config, full) | ||||
| 	sendToHyperion("config","setconfig", '"config":'+JSON.stringify(serverConfig)); | ||||
| } | ||||
|  | ||||
| function requestWriteEffect(effectName,effectPy,effectArgs) | ||||
| function requestWriteEffect(effectName,effectPy,effectArgs,data) | ||||
| { | ||||
| 	var cutArgs = effectArgs.slice(1, -1); | ||||
| 	sendToHyperion("create-effect", "", '"name":"'+effectName+'", "script":"'+effectPy+'", '+cutArgs); | ||||
| 	sendToHyperion("create-effect", "", '"name":"'+effectName+'", "script":"'+effectPy+'", '+cutArgs+',"imageData":"'+data+'"'); | ||||
| } | ||||
|  | ||||
| function requestTestEffect(effectName,effectPy,effectArgs) | ||||
| function requestTestEffect(effectName,effectPy,effectArgs,data) | ||||
| { | ||||
| 	sendToHyperion("effect", "", '"effect":{"name":"'+effectName+'", "args":'+effectArgs+'}, "priority":'+webPrio+', "origin":"'+webOrigin+'", "pythonScript":"'+effectPy+'"'); | ||||
| 	sendToHyperion("effect", "", '"effect":{"name":"'+effectName+'", "args":'+effectArgs+'}, "priority":'+webPrio+', "origin":"'+webOrigin+'", "pythonScript":"'+effectPy+'", "imageData":"'+data+'"'); | ||||
| } | ||||
|  | ||||
| function requestDeleteEffect(effectName) | ||||
|   | ||||
| @@ -10,7 +10,7 @@ $(document).ready(function() { | ||||
| 	var canvas_height; | ||||
| 	var canvas_width; | ||||
| 	var twoDPaths = []; | ||||
| 	var toggleLeds = false; | ||||
| 	var toggleLeds, toggleLedsNum = false; | ||||
|  | ||||
| 	/// add prototype for simple canvas clear() method | ||||
| 	CanvasRenderingContext2D.prototype.clear = function(){ | ||||
| @@ -148,9 +148,17 @@ $(document).ready(function() { | ||||
| 			// can be used as fallback when Path2D is not available | ||||
| 			//roundRect(ledsCanvasNodeCtx, led.hscan.minimum * canvas_width, led.vscan.minimum * canvas_height, (led.hscan.maximum-led.hscan.minimum) * canvas_width, (led.vscan.maximum-led.vscan.minimum) * canvas_height, 4, true, colors[idx]) | ||||
| 			//ledsCanvasNodeCtx.fillRect(led.hscan.minimum * canvas_width, led.vscan.minimum * canvas_height, (led.hscan.maximum-led.hscan.minimum) * canvas_width, (led.vscan.maximum-led.vscan.minimum) * canvas_height); | ||||
| 			 | ||||
| 			ledsCanvasNodeCtx.fillStyle = (useColor) ?  "rgba("+colors[idx].red+","+colors[idx].green+","+colors[idx].blue+",0.9)"  : "hsl("+(idx*360/leds.length)+",100%,50%)"; | ||||
| 			ledsCanvasNodeCtx.fill(twoDPaths[idx]); | ||||
| 			ledsCanvasNodeCtx.stroke(twoDPaths[idx]); | ||||
|  | ||||
| 			if(toggleLedsNum) | ||||
| 			{ | ||||
| 				ledsCanvasNodeCtx.fillStyle = "blue"; | ||||
| 				ledsCanvasNodeCtx.textAlign = "center"; | ||||
| 				ledsCanvasNodeCtx.fillText(idx, (led.hscan.minimum * canvas_width) + ( ((led.hscan.maximum-led.hscan.minimum) * canvas_width) / 2), (led.vscan.minimum * canvas_height) + ( ((led.vscan.maximum-led.vscan.minimum) * canvas_height) / 2)); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -173,12 +181,11 @@ $(document).ready(function() { | ||||
| 		resetImage() | ||||
| 	} | ||||
|  | ||||
| 	// -----------------------FIX THIS------------------------- | ||||
| 	// ------------------------------------------------------------------ | ||||
| 	// $('#leds_toggle_num').off().on("click", function() { | ||||
| 	//	$('.led_num').toggle(); | ||||
| 	//	toggleClass('#leds_toggle_num', "btn-danger", "btn-success"); | ||||
| 	//}); | ||||
| 	$('#leds_toggle_num').off().on("click", function() { | ||||
| 		toggleLedsNum = !toggleLedsNum | ||||
| 		toggleClass('#leds_toggle_num', "btn-danger", "btn-success"); | ||||
| 	}); | ||||
| 	// ------------------------------------------------------------------ | ||||
|  | ||||
| 	$('#leds_toggle').off().on("click", function() { | ||||
|   | ||||
| @@ -5982,24 +5982,8 @@ JSONEditor.defaults.editors.upload = JSONEditor.AbstractEditor.extend({ | ||||
|     if(!this.preview_value) return; | ||||
|  | ||||
|     var self = this; | ||||
|  | ||||
|     var mime = this.preview_value.match(/^data:([^;,]+)[;,]/); | ||||
|     if(mime) mime = mime[1]; | ||||
|     if(!mime) mime = 'unknown'; | ||||
|  | ||||
|     var file = this.uploader.files[0]; | ||||
|  | ||||
|     this.preview.innerHTML = '<strong>Type:</strong> '+mime+', <strong>Size:</strong> '+file.size+' bytes'; | ||||
|     if(mime.substr(0,5)==="image") { | ||||
|       this.preview.innerHTML += '<br>'; | ||||
|       var img = document.createElement('img'); | ||||
|       img.style.maxWidth = '100%'; | ||||
|       img.style.maxHeight = '100px'; | ||||
|       img.src = this.preview_value; | ||||
|       this.preview.appendChild(img); | ||||
|     } | ||||
|  | ||||
|     this.preview.innerHTML += '<br>'; | ||||
|     var uploadButton = this.getButton('Upload', 'upload', 'Upload'); | ||||
|     this.preview.appendChild(uploadButton); | ||||
|     uploadButton.addEventListener('click',function(event) { | ||||
| @@ -6036,6 +6020,11 @@ JSONEditor.defaults.editors.upload = JSONEditor.AbstractEditor.extend({ | ||||
|         } | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     if(this.jsoneditor.options.auto_upload || this.schema.options.auto_upload) { | ||||
|       uploadButton.dispatchEvent(new MouseEvent('click')); | ||||
|       this.preview.removeChild(uploadButton); | ||||
|     } | ||||
|   }, | ||||
|   enable: function() { | ||||
|     if(this.uploader) this.uploader.disabled = false; | ||||
| @@ -6825,10 +6814,7 @@ JSONEditor.defaults.themes.bootstrap3 = JSONEditor.AbstractTheme.extend({ | ||||
|   }, | ||||
|   getButton: function(text, icon, title) { | ||||
|     var el = this._super(text, icon, title); | ||||
| 	if(icon.className.includes("fa-times")) | ||||
| 		el.className += 'btn btn-sm btn-danger'; | ||||
| 	else | ||||
| 		el.className += 'btn btn-sm btn-primary'; | ||||
|     el.className += 'btn btn-default'; | ||||
|     return el; | ||||
|   }, | ||||
|   getTable: function() { | ||||
|   | ||||
| @@ -316,8 +316,6 @@ function createJsonEditor(container,schema,setconfig,usePanel,arrayre) | ||||
| 	$('#'+container).off(); | ||||
| 	$('#'+container).html(""); | ||||
|  | ||||
| 	//JSONEditor.plugins.selectize.enable = true; | ||||
|  | ||||
| 	if (typeof arrayre === 'undefined') | ||||
| 		arrayre = true; | ||||
|  | ||||
|   | ||||
							
								
								
									
										112
									
								
								bin/scripts/docker-compile.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								bin/scripts/docker-compile.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| #!/bin/bash -e | ||||
|  | ||||
| DOCKER="docker" | ||||
| # Git repo url of Hyperion | ||||
| GIT_REPO_URL="https://github.com/hyperion-project/hyperion.ng.git" | ||||
| # cmake build type | ||||
| BUILD_TYPE="Release" | ||||
| # the image tag at hyperionorg/hyperion-ci | ||||
| BUILD_TARGET="ubuntu1604" | ||||
| # build packages (.deb .zip ...) | ||||
| BUILD_PACKAGES=true | ||||
| # packages string inserted to cmake cmd | ||||
| PACKAGES="" | ||||
|  | ||||
| # get current path to this script, independent of calling | ||||
| pushd . > /dev/null | ||||
| SCRIPT_PATH="${BASH_SOURCE[0]}" | ||||
| if ([ -h "${SCRIPT_PATH}" ]); then | ||||
|   while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`; | ||||
|   SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done | ||||
| fi | ||||
| cd `dirname ${SCRIPT_PATH}` > /dev/null | ||||
| SCRIPT_PATH=`pwd`; | ||||
| popd  > /dev/null | ||||
|  | ||||
| set +e | ||||
| $DOCKER ps >/dev/null 2>&1 | ||||
| if [ $? != 0 ]; then | ||||
| 	DOCKER="sudo docker" | ||||
| fi | ||||
| # check if docker is available | ||||
| if ! $DOCKER ps >/dev/null; then | ||||
| 	echo "Error connecting to docker:" | ||||
| 	$DOCKER ps | ||||
| 	printHelp | ||||
| 	exit 1 | ||||
| fi | ||||
| set -e | ||||
|  | ||||
| # help print function | ||||
| 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 Ubuntu 16.04 (x64) or higher. | ||||
| ## Supports Raspberry Pi (armv6) cross compilation (Raspbian Stretch) | ||||
| ## | ||||
| ## Homepage: https://www.hyperion-project.org | ||||
| ## Forum: https://forum.hyperion-project.org | ||||
| ######################################################## | ||||
| # These are possible arguments to modify the script behaviour with their default values | ||||
| # | ||||
| # docker-compile.sh -h	            # Show this help message | ||||
| # docker-compile.sh -t ubuntu1604	# The docker tag, one of ubuntu1604 | cross-qemu-rpistretch | ||||
| # docker-compile.sh -b Release      # cmake Release or Debug build | ||||
| # docker-compile.sh -p true         # If true build packages with CPack | ||||
| # More informations to docker tags at: https://hub.docker.com/r/hyperionorg/hyperion-ci/" | ||||
| } | ||||
|  | ||||
| while getopts t:b:p:h option | ||||
| do | ||||
|  case "${option}" | ||||
|  in | ||||
|  t) BUILD_TARGET=${OPTARG};; | ||||
|  b) BUILD_TYPE=${OPTARG};; | ||||
|  p) BUILD_PACKAGES=${OPTARG};; | ||||
|  h) printHelp; exit 0;; | ||||
|  esac | ||||
| done | ||||
|  | ||||
| # determine package creation | ||||
| if [ $BUILD_PACKAGES == "true" ]; then | ||||
| 	PACKAGES="package" | ||||
| fi | ||||
|  | ||||
| echo "---> Initilize with BUILD_TARGET=${BUILD_TARGET}, BUILD_TYPE=${BUILD_TYPE}, BUILD_PACKAGES=${BUILD_PACKAGES}" | ||||
|  | ||||
| # cleanup deploy folder, create folder for ownership | ||||
| sudo rm -fr $SCRIPT_PATH/deploy >/dev/null 2>&1 | ||||
| mkdir $SCRIPT_PATH/deploy >/dev/null 2>&1 | ||||
|  | ||||
| # get Hyperion source, cleanup previous folder | ||||
| echo "---> Downloading Hyperion source code from ${GIT_REPO_URL}" | ||||
| sudo rm -fr $SCRIPT_PATH/hyperion >/dev/null 2>&1 | ||||
| git clone --recursive --depth 1 -q -b rework $GIT_REPO_URL $SCRIPT_PATH/hyperion || { echo "---> Failed to download Hyperion source code! Abort"; exit 1; } | ||||
|  | ||||
| # start compilation | ||||
| # Remove container after stop | ||||
| # Mount /deploy to /deploy | ||||
| # Mount source dir to /source | ||||
| # Target docker image | ||||
| # execute inside container all commands on bash | ||||
| echo "---> Startup docker..." | ||||
| $DOCKER run --rm \ | ||||
| 	-v "${SCRIPT_PATH}/deploy:/deploy" \ | ||||
| 	-v "${SCRIPT_PATH}/hyperion:/source:ro" \ | ||||
| 	hyperionorg/hyperion-ci:$BUILD_TARGET \ | ||||
| 	/bin/bash -c "mkdir build && cp -r /source/. /build && | ||||
| 	cd /build && mkdir build && cd build && | ||||
| 	cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. || exit 2 && | ||||
| 	make -j $(nproc) ${PACKAGES} || exit 3 && | ||||
| 	echo '---> Copy binaries and packages to host folder: ${SCRIPT_PATH}/deploy' && | ||||
| 	cp -v /build/build/bin/h* /deploy/ 2>/dev/null || : && | ||||
| 	cp -v /build/build/Hyperion-* /deploy/ 2>/dev/null || : && | ||||
| 	exit 0; | ||||
| 	exit 1 " || { echo "---> Hyperion compilation failed! Abort"; exit 4; } | ||||
|  | ||||
| # overwrite file owner to current user | ||||
| sudo chown -fR $(stat -c "%U:%G" $SCRIPT_PATH/deploy) $SCRIPT_PATH/deploy | ||||
|  | ||||
| echo "---> Script finished, view folder ${SCRIPT_PATH}/deploy for compiled packages and binaries" | ||||
| exit 0 | ||||
| @@ -1,165 +0,0 @@ | ||||
| #!/bin/bash | ||||
| # Script to add a second or more hyperion instance(s) to the corresponding system service | ||||
|  | ||||
| # Make sure /sbin is on the path (for service to find sub scripts) | ||||
| PATH="/sbin:$PATH" | ||||
|  | ||||
| #Check, if script is running as root | ||||
| if [ $(id -u) != 0 ]; then | ||||
| 		echo '---> Critical Error: Please run the script as root (sudo sh ./setup_hyperion_forward.sh) -> abort'  | ||||
| 		exit 1 | ||||
| fi | ||||
|  | ||||
| #Welcome message | ||||
| echo '*******************************************************************************'  | ||||
| echo 'This setup script will duplicate the hyperion service' | ||||
| echo 'Choose the name(s) for one or more config files - one service for each config'  | ||||
| echo 'Created by brindosch - hyperion-project.org - the official Hyperion source.'  | ||||
| echo '*******************************************************************************' | ||||
|  | ||||
| #Prompt for confirmation to proceed | ||||
| while true | ||||
| do | ||||
| echo -n "---> Do you really want to proceed? (y or n) :" | ||||
| read CONFIRM | ||||
| case $CONFIRM in | ||||
| y|Y|YES|yes|Yes) break ;; | ||||
| n|N|no|NO|No) | ||||
| echo "---> Aborting - you entered \"$CONFIRM\"" | ||||
| exit | ||||
| ;; | ||||
| *) echo "-> Please enter only y or n" | ||||
| esac | ||||
| done | ||||
| echo "---> You entered \"$CONFIRM\". We will proceed!" | ||||
| echo "" | ||||
|  | ||||
| #Check which system we are on | ||||
| OS_OPENELEC=`grep -m1 -c 'OpenELEC\|RasPlex\|LibreELEC' /etc/issue` | ||||
| USE_SYSTEMD=`grep -m1 -c systemd /proc/1/comm` | ||||
| USE_INITCTL=`which /sbin/initctl | wc -l` | ||||
| USE_SERVICE=`which /usr/sbin/service | wc -l` | ||||
|  | ||||
| #Setting up the paths to service files | ||||
| if [ $USE_INITCTL -eq 1 ]; then | ||||
| 	SERVICEPATH=/etc/init | ||||
| elif [ $OS_OPENELEC -eq 1 ]; then | ||||
| 	SERVICEPATH=/storage/.config | ||||
| elif [ $USE_SYSTEMD -eq 1 ]; then | ||||
| 	SERVICEPATH=/etc/systemd/system | ||||
| elif [ $USE_SERVICE -eq 1 ]; then | ||||
| 	SERVICEPATH/etc/init.d | ||||
| fi | ||||
|  | ||||
| #Setting up the default PROTO/JSON ports | ||||
| JSONPORT=19444 | ||||
| PROTOPORT=19445 | ||||
| # and service count | ||||
| SERVICEC=1 | ||||
|  | ||||
| #Setting up the paths to config files | ||||
| if [ $OS_OPENELEC -eq 1 ]; then | ||||
| 	CONFIGPATH=/storage/.config | ||||
| else CONFIGPATH=/opt/hyperion/config | ||||
| fi | ||||
|  | ||||
| #Ask the user for some informations regarding the setup | ||||
| echo "---> Please enter the config name(s) you want to create" | ||||
| echo "---> Information: One name creates one service and two names two services etc" | ||||
| echo '---> Please enter them seperated with a space in a one line row!' | ||||
| echo '---> example: hyperion.philipshue_1.json hyperion.AtmoOrb_2.json hypthreeconf.json' | ||||
| echo '---> In any case, add ".json" at the end of each file name' | ||||
| read -p 'Config file name(s): ' FILENAMES | ||||
| echo '---> Thank you, we will modify your Hyperion installation now' | ||||
| sleep 2 | ||||
|  | ||||
| #Processing input | ||||
| set $FILENAMES | ||||
| FWCOUNT=${#} | ||||
|  | ||||
| #Convert all old config file paths to make sure this script is working (default for new installs with 1.02.0 and higher) | ||||
| if [ $USE_INITCTL -eq 1 ]; then | ||||
| 	sed -i "s|/etc/hyperion.config.json|/etc/hyperion/hyperion.config.json|g" $SERVICEPATH/hyperion.conf | ||||
| elif [ $OS_OPENELEC -eq 1 ]; then | ||||
| 	sleep 0 | ||||
| elif [ $USE_SYSTEMD -eq 1 ]; then | ||||
| 	sed -i "s|/etc/hyperion.config.json|/etc/hyperion/hyperion.config.json|g" $SERVICEPATH/hyperion.service | ||||
| elif [ $USE_SERVICE -eq 1 ]; then | ||||
| 	sed -i "s|/etc/hyperion.config.json|/etc/hyperion/hyperion.config.json|g" $SERVICEPATH/hyperion | ||||
| fi | ||||
|  | ||||
| #Processing service files | ||||
| if [ $USE_INITCTL -eq 1 ]; then | ||||
| 	echo "---> Initctl detected, processing service files" | ||||
| 	while [ $SERVICEC -le $FWCOUNT ]; do | ||||
| 	echo "Processing service ${SERVICEC}: \"hyperion_fw${SERVICEC}.conf\"" | ||||
| 		if [ -e "${SERVICEPATH}/hyperion_fw${SERVICEC}.conf" ]; then | ||||
| 			echo "Service was already created - skipped" | ||||
| 			echo "Input \"${1}\" was skipped" | ||||
| 		else	 | ||||
| 			echo "Create ${SERVICEPATH}/hyperion_fw${SERVICEC}.conf" | ||||
| 			cp -s $SERVICEPATH/hyperion.conf $SERVICEPATH/hyperion_fw$SERVICEC.conf | ||||
| 			echo "Config name changed to \"${1}\" inside \"hyperion_fw${SERVICEC}.conf\"" | ||||
| 			sed -i "s/hyperion.config.json/$1/g" $SERVICEPATH/hyperion_fw$SERVICEC.conf | ||||
| 			initctl reload-configuration | ||||
| 		fi | ||||
| 	shift | ||||
| 	SERVICEC=$((SERVICEC + 1)) | ||||
| 	done | ||||
| elif [ $OS_OPENELEC -eq 1 ]; then | ||||
| 	echo "---> OE/LE detected, processing autostart.sh" | ||||
| 	while [ $SERVICEC -le $FWCOUNT ]; do | ||||
| 	echo "${SERVICEC}. processing OE autostart.sh entry \"${1}\"" | ||||
| 		OE=`grep -m1 -c ${1} $SERVICEPATH/autostart.sh` | ||||
| 		if [ $OE -eq 0 ]; then | ||||
| 			echo "Add config name \"${1}\" to \"autostart.sh\"" | ||||
| 			echo "/storage/hyperion/bin/hyperiond.sh /storage/.config/${1} > /storage/logfiles/hyperion_fw${SERVICEC}.log 2>&1 &" >> /storage/.config/autostart.sh | ||||
| 		else | ||||
| 			echo "\"${1}\" was already added - skipped" | ||||
| 		fi | ||||
| 	shift | ||||
| 	SERVICEC=$((SERVICEC + 1)) | ||||
| 	done	 | ||||
| elif [ $USE_SYSTEMD -eq 1 ]; then | ||||
| 	echo "---> Systemd detected, processing service files" | ||||
| 	while [ $SERVICEC -le $FWCOUNT ]; do | ||||
| 	echo "Processing service ${SERVICEC}: \"hyperion_fw${SERVICEC}.service\"" | ||||
| 		if [ -e "${SERVICEPATH}/hyperion_fw${SERVICEC}.service" ]; then | ||||
| 			echo "Service was already created - skipped" | ||||
| 			echo "Input \"${1}\" was skipped" | ||||
| 		else | ||||
| 			echo "Create ${SERVICEPATH}/hyperion_fw${SERVICEC}.service" | ||||
| 			cp -s $SERVICEPATH/hyperion.service $SERVICEPATH/hyperion_fw$SERVICEC.service | ||||
| 			echo "Config name changed to \"${1}\" inside \"hyperion_fw${SERVICEC}.service\"" | ||||
| 			sed -i "s/hyperion.config.json/$1/g" $SERVICEPATH/hyperion_fw$SERVICEC.service | ||||
| 			systemctl -q enable hyperion_fw$SERVICEC.service | ||||
| 		fi | ||||
| 	shift | ||||
| 	SERVICEC=$((SERVICEC + 1)) | ||||
| 	done | ||||
| elif [ $USE_SERVICE -eq 1 ]; then | ||||
| 	echo "---> Init.d detected, processing service files" | ||||
| 	while [ $SERVICEC -le $FWCOUNT ]; do | ||||
| 	echo "Processing service ${SERVICEC}: \"hyperion_fw${SERVICEC}\"" | ||||
| 		if [ -e "${SERVICEPATH}/hyperion_fw${SERVICEC}" ]; then | ||||
| 			echo "Service was already created - skipped" | ||||
| 			echo "Input \"${1}\" was skipped" | ||||
| 		else | ||||
| 			echo "Create ${SERVICEPATH}/hyperion_fw${SERVICEC}" | ||||
| 			cp -s $SERVICEPATH/hyperion $SERVICEPATH/hyperion_fw$SERVICEC | ||||
| 			echo "Config name changed to \"${1}\" inside \"hyperion_fw${SERVICEC}\"" | ||||
| 			sed -i "s/hyperion.config.json/$1/g" $SERVICEPATH/hyperion_fw$SERVICEC | ||||
| 			update-rc.d hyperion_fw$SERVICEC defaults 98 02 | ||||
| 		fi | ||||
| 	shift | ||||
| 	SERVICEC=$((SERVICEC + 1)) | ||||
| 	done	 | ||||
| fi | ||||
|  | ||||
| #Service creation done | ||||
| echo '*******************************************************************************' | ||||
| echo 'Script done all actions - all input processed' | ||||
| echo 'Now upload your configuration(s) with HyperCon at the SSH Tab' | ||||
| echo 'All created Hyperion services will start with your chosen confignames' | ||||
| echo 'Wiki: wiki.hyperion-project.org Webpage: www.hyperion-project.org'  | ||||
| echo '*******************************************************************************' | ||||
| @@ -14,7 +14,7 @@ ENDIF() | ||||
| SET ( CPACK_PACKAGE_NAME "Hyperion" ) | ||||
| SET ( CPACK_PACKAGE_DESCRIPTION_SUMMARY "Hyperion is an open source ambient light implementation" ) | ||||
| SET ( CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md" ) | ||||
| SET ( CPACK_PACKAGE_FILE_NAME "Hyperion-${HYPERION_VERSION_MAJOR}.${HYPERION_VERSION_MINOR}.${HYPERION_VERSION_PATCH}") | ||||
| SET ( CPACK_PACKAGE_FILE_NAME "Hyperion-${HYPERION_VERSION_MAJOR}.${HYPERION_VERSION_MINOR}.${HYPERION_VERSION_PATCH}-${CMAKE_SYSTEM_NAME}") | ||||
| SET ( CPACK_PACKAGE_CONTACT "packages@hyperion-project.org") | ||||
| SET ( CPACK_PACKAGE_EXECUTABLES "hyperiond;Hyperion" ) | ||||
| SET ( CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/icons/hyperion-icon-32px.png") | ||||
|   | ||||
| @@ -136,7 +136,7 @@ | ||||
| 	], | ||||
|  | ||||
| 	///  The configuration for the frame-grabber, contains the following items: | ||||
| 	///   * type         : type of grabber. (auto|osx|dispmanx|amlogic|x11|framebuffer) [auto] | ||||
| 	///   * type         : type of grabber. (auto|osx|dispmanx|amlogic|x11|framebuffer|qt) [auto] | ||||
| 	///   * width        : The width of the grabbed frames [pixels] | ||||
| 	///   * height       : The height of the grabbed frames [pixels] | ||||
| 	///   * frequency_Hz : The frequency of the frame grab [Hz] | ||||
| @@ -155,9 +155,12 @@ | ||||
| 		"width"        : 96, | ||||
| 		"height"       : 96, | ||||
|  | ||||
| 		// valid for x11 | ||||
| 		// valid for x11|qt | ||||
| 		"pixelDecimation"           : 8, | ||||
|  | ||||
| 		// valid for qt | ||||
| 		"display" 0, | ||||
|  | ||||
| 		// valid for framebuffer | ||||
| 		"device"     : "/dev/fb0" | ||||
| 	}, | ||||
|   | ||||
| @@ -3,11 +3,16 @@ | ||||
| 	"script" : "gif.py", | ||||
| 	"title":"edt_eff_gif_header", | ||||
| 	"required":true, | ||||
| 	"properties":{ | ||||
| 	"properties": { | ||||
| 		"image": { | ||||
| 			"type": "string", | ||||
| 			"title":"edt_eff_image", | ||||
| 			"format" : "file", | ||||
| 			"format" : "url", | ||||
| 			"options" : | ||||
| 			{ | ||||
| 				"upload" : true, | ||||
| 				"auto_upload" : true | ||||
| 			}, | ||||
| 			"default": "", | ||||
| 			"propertyOrder" : 1 | ||||
| 		}, | ||||
|   | ||||
| @@ -114,7 +114,7 @@ private: | ||||
| 	/// | ||||
| 	/// @param message the incoming message | ||||
| 	/// | ||||
| 	void handleEffectCommand(const QJsonObject & message, const QString &command, const int tan); | ||||
| 	void handleEffectCommand(const QJsonObject &message, const QString &command, const int tan); | ||||
|  | ||||
| 	/// | ||||
| 	/// Handle an incoming JSON Effect message (Write JSON Effect) | ||||
|   | ||||
| @@ -89,10 +89,9 @@ namespace hyperion | ||||
| 			// find first X pixel of the image | ||||
| 			for (int x = 0; x < width33percent; ++x) | ||||
| 			{ | ||||
| 				const Pixel_T & color1 = image( (width - x), yCenter); // right side center line check | ||||
| 				const Pixel_T & color2 = image(x, height33percent); | ||||
| 				const Pixel_T & color3 = image(x, height66percent); | ||||
| 				if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3)) | ||||
| 				if (!isBlack(image((width - x), yCenter)) | ||||
| 					|| !isBlack(image(x, height33percent)) | ||||
| 				 	|| !isBlack(image(x, height66percent))) | ||||
| 				{ | ||||
| 					firstNonBlackXPixelIndex = x; | ||||
| 					break; | ||||
| @@ -102,10 +101,9 @@ namespace hyperion | ||||
| 			// find first Y pixel of the image | ||||
| 			for (int y = 0; y < height33percent; ++y) | ||||
| 			{ | ||||
| 				const Pixel_T & color1 = image(xCenter, (height - y)); // bottom center line check | ||||
| 				const Pixel_T & color2 = image(width33percent, y ); | ||||
| 				const Pixel_T & color3 = image(width66percent, y); | ||||
| 				if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3)) | ||||
| 				if (!isBlack(image(xCenter, (height - y))) | ||||
| 					|| !isBlack(image(width33percent, y))  | ||||
| 					|| !isBlack(image(width66percent, y))) | ||||
| 				{ | ||||
| 					firstNonBlackYPixelIndex = y; | ||||
| 					break; | ||||
| @@ -203,10 +201,9 @@ namespace hyperion | ||||
| 			int x; | ||||
| 			for (x = 0; x < width33percent; ++x) | ||||
| 			{ | ||||
| 				const Pixel_T & color1 = image( (width - x), yCenter); // right side center line check | ||||
| 				const Pixel_T & color2 = image(x, height33percent); | ||||
| 				const Pixel_T & color3 = image(x, height66percent); | ||||
| 				if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3)) | ||||
| 				if (!isBlack(image((width - x), yCenter))  | ||||
| 					|| !isBlack(image(x, height33percent)) | ||||
| 					|| !isBlack(image(x, height66percent))) | ||||
| 				{ | ||||
| 					firstNonBlackXPixelIndex = x; | ||||
| 					break; | ||||
| @@ -216,13 +213,13 @@ namespace hyperion | ||||
| 			// find first Y pixel of the image | ||||
| 			for (int y = 0; y < height33percent; ++y) | ||||
| 			{ | ||||
| 				const Pixel_T & color1 = image(x, y );// left side top check | ||||
| 				const Pixel_T & color2 = image(x, (height - y)); // left side bottom check | ||||
| 				const Pixel_T & color3 = image( (width - x), y); // right side top check | ||||
| 				const Pixel_T & color4 = image( (width - x), (height - y)); // right side bottom check | ||||
| 				if (!isBlack(color1) || !isBlack(color2) || !isBlack(color3) || !isBlack(color4)) | ||||
| 				// left side top + left side bottom + right side top  +  right side bottom | ||||
| 				if (!isBlack(image(x, y))  | ||||
| 					|| !isBlack(image(x, (height - y))) | ||||
| 					|| !isBlack(image((width - x), y))  | ||||
| 					|| !isBlack(image((width - x), (height - y)))) | ||||
| 				{ | ||||
| //						std::cout << "y " << y << " lt " << int(isBlack(color1)) << " lb " << int(isBlack(color2)) << " rt " << int(isBlack(color3)) << " rb " << int(isBlack(color4)) << std::endl; | ||||
| //					std::cout << "y " << y << " lt " << int(isBlack(color1)) << " lb " << int(isBlack(color2)) << " rt " << int(isBlack(color3)) << " rb " << int(isBlack(color4)) << std::endl; | ||||
| 					firstNonBlackYPixelIndex = y; | ||||
| 					break; | ||||
| 				} | ||||
|   | ||||
| @@ -28,7 +28,14 @@ class Effect : public QThread | ||||
| public: | ||||
| 	friend class EffectModule; | ||||
|  | ||||
| 	Effect(Hyperion* hyperion, int priority, int timeout, const QString & script, const QString & name, const QJsonObject & args = QJsonObject()); | ||||
| 	Effect(Hyperion *hyperion | ||||
| 				, int priority | ||||
| 				, int timeout | ||||
| 				, const QString &script | ||||
| 				, const QString &name | ||||
| 				, const QJsonObject &args = QJsonObject() | ||||
| 				, const QString &imageData = "" | ||||
| 	); | ||||
| 	virtual ~Effect(); | ||||
|  | ||||
| 	virtual void run(); | ||||
| @@ -55,14 +62,14 @@ public: | ||||
| 	QJsonObject getArgs() const { return _args; } | ||||
|  | ||||
| signals: | ||||
| 	void setInput(const int priority, const std::vector<ColorRgb>& ledColors, const int timeout_ms, const bool& clearEffect); | ||||
| 	void setInputImage(const int priority, const Image<ColorRgb>& image, const int timeout_ms, const bool& clearEffect); | ||||
| 	void setInput(const int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms, const bool &clearEffect); | ||||
| 	void setInputImage(const int priority, const Image<ColorRgb> &image, const int timeout_ms, const bool &clearEffect); | ||||
|  | ||||
| private: | ||||
|  | ||||
| 	void addImage(); | ||||
|  | ||||
| 	Hyperion* _hyperion; | ||||
| 	Hyperion *_hyperion; | ||||
|  | ||||
| 	const int _priority; | ||||
|  | ||||
| @@ -72,18 +79,19 @@ private: | ||||
| 	const QString _name; | ||||
|  | ||||
| 	const QJsonObject _args; | ||||
| 	const QString _imageData; | ||||
|  | ||||
| 	int64_t _endTime; | ||||
|  | ||||
| 	/// Buffer for colorData | ||||
| 	QVector<ColorRgb> _colors; | ||||
|  | ||||
| 	Logger* _log; | ||||
| 	Logger *_log; | ||||
| 	// Reflects whenever this effects should interupt (timeout or external request) | ||||
| 	bool _interupt = false; | ||||
|  | ||||
| 	QSize           _imageSize; | ||||
| 	QImage          _image; | ||||
| 	QPainter*       _painter; | ||||
| 	QPainter       *_painter; | ||||
| 	QVector<QImage> _imageStack; | ||||
| }; | ||||
|   | ||||
| @@ -74,7 +74,15 @@ public slots: | ||||
| 	int runEffect(const QString &effectName, int priority, int timeout = -1, const QString &origin="System"); | ||||
|  | ||||
| 	/// Run the specified effect on the given priority channel and optionally specify a timeout | ||||
| 	int runEffect(const QString &effectName, const QJsonObject & args, int priority, int timeout = -1, const QString &pythonScript = "", const QString &origin = "System", unsigned smoothCfg=0); | ||||
| 	int runEffect(const QString &effectName | ||||
| 				, const QJsonObject &args | ||||
| 				, int priority | ||||
| 				, int timeout = -1 | ||||
| 				, const QString &pythonScript = "" | ||||
| 				, const QString &origin = "System" | ||||
| 				, unsigned smoothCfg=0 | ||||
| 				, const QString &imageData = "" | ||||
| 	); | ||||
|  | ||||
| 	/// Clear any effect running on the provided channel | ||||
| 	void channelCleared(int priority); | ||||
| @@ -92,7 +100,15 @@ private slots: | ||||
|  | ||||
| private: | ||||
| 	/// Run the specified effect on the given priority channel and optionally specify a timeout | ||||
| 	int runEffectScript(const QString &script, const QString &name, const QJsonObject & args, int priority, int timeout = -1, const QString & origin="System", unsigned smoothCfg=0); | ||||
| 	int runEffectScript(const QString &script | ||||
| 				,const QString &name | ||||
| 				, const QJsonObject &args | ||||
| 				, int priority | ||||
| 				, int timeout = -1 | ||||
| 				, const QString &origin="System" | ||||
| 				, unsigned smoothCfg=0 | ||||
| 				, const QString &imageData = "" | ||||
| 	); | ||||
|  | ||||
| private: | ||||
| 	Hyperion * _hyperion; | ||||
|   | ||||
							
								
								
									
										96
									
								
								include/grabber/QtGrabber.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								include/grabber/QtGrabber.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <QObject> | ||||
|  | ||||
| // Hyperion-utils includes | ||||
| #include <utils/ColorRgb.h> | ||||
| #include <hyperion/Grabber.h> | ||||
|  | ||||
| class QScreen; | ||||
|  | ||||
| /// | ||||
| /// @brief The platform capture implementation based on QT API | ||||
| /// | ||||
| class QtGrabber : public Grabber | ||||
| { | ||||
| public: | ||||
|  | ||||
| 	QtGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display); | ||||
|  | ||||
| 	virtual ~QtGrabber(); | ||||
|  | ||||
| 	/// | ||||
| 	/// Captures a single snapshot of the display and writes the data to the given image. The | ||||
| 	/// provided image should have the same dimensions as the configured values (_width and | ||||
| 	/// _height) | ||||
| 	/// | ||||
| 	/// @param[out] image  The snapped screenshot (should be initialized with correct width and | ||||
| 	/// height) | ||||
| 	/// | ||||
| 	virtual int grabFrame(Image<ColorRgb> & image); | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Set a new video mode | ||||
| 	/// | ||||
| 	virtual void setVideoMode(VideoMode mode); | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Apply new width/height values, overwrite Grabber.h implementation as qt doesn't use width/height, just pixelDecimation to calc dimensions | ||||
| 	/// | ||||
| 	virtual bool setWidthHeight(int width, int height) { return true; }; | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Apply new pixelDecimation | ||||
| 	/// | ||||
| 	virtual void setPixelDecimation(int pixelDecimation); | ||||
|  | ||||
| 	/// | ||||
| 	/// Set the crop values | ||||
| 	/// @param  cropLeft    Left pixel crop | ||||
| 	/// @param  cropRight   Right pixel crop | ||||
| 	/// @param  cropTop     Top pixel crop | ||||
| 	/// @param  cropBottom  Bottom pixel crop | ||||
| 	/// | ||||
| 	virtual void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom); | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Apply display index | ||||
| 	/// | ||||
| 	virtual void setDisplayIndex(int index); | ||||
|  | ||||
| private slots: | ||||
| 	/// | ||||
| 	/// @brief is called whenever the current _screen changes it's geometry | ||||
| 	/// @param geo   The new geometry | ||||
| 	/// | ||||
| 	void geometryChanged(const QRect &geo); | ||||
|  | ||||
| private: | ||||
| 	/// | ||||
| 	/// @brief Setup a new capture display, will free the previous one | ||||
| 	/// @return True on success, false if no display is found | ||||
| 	/// | ||||
| 	const bool setupDisplay(); | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Is called whenever we need new screen dimension calculations based on window geometry | ||||
| 	/// | ||||
| 	int updateScreenDimensions(const bool& force); | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief free the _screen pointer | ||||
| 	/// | ||||
| 	void freeResources(); | ||||
|  | ||||
| private: | ||||
|  | ||||
| 	unsigned _display; | ||||
| 	int _pixelDecimation; | ||||
| 	unsigned _screenWidth; | ||||
| 	unsigned _screenHeight; | ||||
| 	unsigned _src_x; | ||||
| 	unsigned _src_y; | ||||
| 	unsigned _src_x_max; | ||||
| 	unsigned _src_y_max; | ||||
| 	QScreen* _screen; | ||||
| }; | ||||
							
								
								
									
										38
									
								
								include/grabber/QtWrapper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								include/grabber/QtWrapper.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <hyperion/GrabberWrapper.h> | ||||
| #include <grabber/QtGrabber.h> | ||||
|  | ||||
| /// | ||||
| /// The QtWrapper uses QtFramework API's to get a picture from system | ||||
| /// | ||||
| class QtWrapper: public GrabberWrapper | ||||
| { | ||||
| public: | ||||
| 	/// | ||||
| 	/// Constructs the framebuffer frame grabber with a specified grab size and update rate. | ||||
| 	/// | ||||
| 	/// @param[in] cropLeft          Remove from left [pixels] | ||||
| 	/// @param[in] cropRight 	     Remove from right [pixels] | ||||
| 	/// @param[in] cropTop           Remove from top [pixels] | ||||
| 	/// @param[in] cropBottom        Remove from bottom [pixels] | ||||
| 	/// @param[in] pixelDecimation   Decimation factor for image [pixels] | ||||
| 	/// @param[in] updateRate_Hz     The image grab rate [Hz] | ||||
| 	/// | ||||
| 	QtWrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display, const unsigned updateRate_Hz); | ||||
|  | ||||
| 	/// | ||||
| 	/// Destructor of this qt frame grabber. Releases any claimed resources. | ||||
| 	/// | ||||
| 	virtual ~QtWrapper() {}; | ||||
|  | ||||
| public slots: | ||||
| 	/// | ||||
| 	/// Performs a single frame grab and computes the led-colors | ||||
| 	/// | ||||
| 	virtual void action(); | ||||
|  | ||||
| private: | ||||
| 	/// The actual grabber | ||||
| 	QtGrabber _grabber; | ||||
| }; | ||||
| @@ -39,7 +39,7 @@ public: | ||||
| 	virtual bool setWidthHeight(int width, int height); | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Apply new pixelDecimation (used from x11) | ||||
| 	/// @brief Apply new pixelDecimation (used from x11 and qt) | ||||
| 	/// | ||||
| 	virtual void setPixelDecimation(int pixelDecimation) {}; | ||||
|  | ||||
| @@ -71,7 +71,7 @@ public: | ||||
| 	virtual void setDeviceVideoStandard(QString device, VideoStandard videoStandard) {}; | ||||
|  | ||||
| 	/// | ||||
| 	/// @brief Apply display index (used from x11) | ||||
| 	/// @brief Apply display index (used from qt) | ||||
| 	/// | ||||
| 	virtual void setDisplayIndex(int index) {}; | ||||
|  | ||||
|   | ||||
| @@ -360,8 +360,14 @@ public slots: | ||||
| 	/// @param args arguments of the effect script | ||||
| 	///	@param priority The priority channel of the effect | ||||
| 	/// @param timeout The timeout of the effect (after the timout, the effect will be cleared) | ||||
| 	int setEffect(const QString & effectName, const QJsonObject & args, int priority, | ||||
| 				  int timeout = -1, const QString & pythonScript = "", const QString & origin="System"); | ||||
| 	int setEffect(const QString &effectName | ||||
| 				, const QJsonObject &args | ||||
| 				, int priority | ||||
| 				, int timeout = -1 | ||||
| 				, const QString &pythonScript = "" | ||||
| 				, const QString &origin="System" | ||||
| 				, const QString &imageData = "" | ||||
| 	); | ||||
|  | ||||
| 	/// sets the methode how image is maped to leds at ImageProcessor | ||||
| 	void setLedMappingType(const int& mappingType); | ||||
|   | ||||
| @@ -168,7 +168,9 @@ namespace hyperion | ||||
| 		template <typename Pixel_T> | ||||
| 		ColorRgb calcMeanColor(const Image<Pixel_T> & image, const std::vector<unsigned> & colors) const | ||||
| 		{ | ||||
| 			if (colors.size() == 0) | ||||
| 			const auto colorVecSize = colors.size(); | ||||
|  | ||||
| 			if (colorVecSize == 0) | ||||
| 			{ | ||||
| 				return ColorRgb::BLACK; | ||||
| 			} | ||||
| @@ -177,18 +179,20 @@ namespace hyperion | ||||
| 			uint_fast16_t cummRed   = 0; | ||||
| 			uint_fast16_t cummGreen = 0; | ||||
| 			uint_fast16_t cummBlue  = 0; | ||||
| 			const auto& imgData = image.memptr(); | ||||
|  | ||||
| 			for (const unsigned colorOffset : colors) | ||||
| 			{ | ||||
| 				const Pixel_T& pixel = image.memptr()[colorOffset]; | ||||
| 				const auto& pixel = imgData[colorOffset]; | ||||
| 				cummRed   += pixel.red; | ||||
| 				cummGreen += pixel.green; | ||||
| 				cummBlue  += pixel.blue; | ||||
| 			} | ||||
|  | ||||
| 			// Compute the average of each color channel | ||||
| 			const uint8_t avgRed   = uint8_t(cummRed/colors.size()); | ||||
| 			const uint8_t avgGreen = uint8_t(cummGreen/colors.size()); | ||||
| 			const uint8_t avgBlue  = uint8_t(cummBlue/colors.size()); | ||||
| 			const uint8_t avgRed   = uint8_t(cummRed/colorVecSize); | ||||
| 			const uint8_t avgGreen = uint8_t(cummGreen/colorVecSize); | ||||
| 			const uint8_t avgBlue  = uint8_t(cummBlue/colorVecSize); | ||||
|  | ||||
| 			// Return the computed color | ||||
| 			return {avgRed, avgGreen, avgBlue}; | ||||
| @@ -211,9 +215,11 @@ namespace hyperion | ||||
| 			uint_fast16_t cummBlue  = 0; | ||||
| 			const unsigned imageSize = image.width() * image.height(); | ||||
|  | ||||
| 			const auto& imgData = image.memptr(); | ||||
|  | ||||
| 			for (unsigned idx=0; idx<imageSize; idx++) | ||||
| 			{ | ||||
| 				const Pixel_T& pixel = image.memptr()[idx]; | ||||
| 				const auto& pixel = imgData[idx]; | ||||
| 				cummRed   += pixel.red; | ||||
| 				cummGreen += pixel.green; | ||||
| 				cummBlue  += pixel.blue; | ||||
|   | ||||
| @@ -24,6 +24,10 @@ | ||||
| 		{ | ||||
| 			"type" : "object", | ||||
| 			"required" : true | ||||
| 		}, | ||||
| 		"imageData" : { | ||||
| 			"type" : "string", | ||||
| 			"required" : false | ||||
| 		} | ||||
| 	}, | ||||
| 	"additionalProperties": false | ||||
|   | ||||
| @@ -44,6 +44,10 @@ | ||||
| 		"pythonScript" : { | ||||
| 			"type" : "string", | ||||
| 			"required" : false | ||||
| 		}, | ||||
| 		"imageData" : { | ||||
| 			"type" : "string", | ||||
| 			"required" : false | ||||
| 		} | ||||
| 	}, | ||||
| 	"additionalProperties": false | ||||
|   | ||||
| @@ -12,9 +12,6 @@ | ||||
| #include <QImage> | ||||
| #include <QBuffer> | ||||
| #include <QByteArray> | ||||
| // #include <QFileInfo> | ||||
| // #include <QDir> | ||||
| // #include <QIODevice> | ||||
| #include <QDateTime> | ||||
|  | ||||
| // hyperion includes | ||||
| @@ -180,7 +177,7 @@ void JsonAPI::handleImageCommand(const QJsonObject& message, const QString& comm | ||||
| 	sendSuccessReply(command, tan); | ||||
| } | ||||
|  | ||||
| void JsonAPI::handleEffectCommand(const QJsonObject& message, const QString& command, const int tan) | ||||
| void JsonAPI::handleEffectCommand(const QJsonObject &message, const QString &command, const int tan) | ||||
| { | ||||
| 	emit forwardJsonMessage(message); | ||||
|  | ||||
| @@ -191,16 +188,12 @@ void JsonAPI::handleEffectCommand(const QJsonObject& message, const QString& com | ||||
| 	QString origin = message["origin"].toString() + "@"+_peerAddress; | ||||
| 	const QJsonObject & effect = message["effect"].toObject(); | ||||
| 	const QString & effectName = effect["name"].toString(); | ||||
| 	const QString & data = message["imageData"].toString("").toUtf8(); | ||||
|  | ||||
| 	// set output | ||||
| 	if (effect.contains("args")) | ||||
| 	{ | ||||
| 		_hyperion->setEffect(effectName, effect["args"].toObject(), priority, duration, pythonScript, origin); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		_hyperion->setEffect(effectName, priority, duration, origin); | ||||
| 	} | ||||
| 	 (effect.contains("args")) | ||||
| 		? _hyperion->setEffect(effectName, effect["args"].toObject(), priority, duration, pythonScript, origin, data) | ||||
| 		: _hyperion->setEffect(effectName, priority, duration, origin); | ||||
|  | ||||
| 	// send reply | ||||
| 	sendSuccessReply(command, tan); | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
| //impl | ||||
| PyThreadState* mainThreadState; | ||||
|  | ||||
| Effect::Effect(Hyperion* hyperion, int priority, int timeout, const QString & script, const QString & name, const QJsonObject & args) | ||||
| Effect::Effect(Hyperion *hyperion, int priority, int timeout, const QString &script, const QString &name, const QJsonObject &args, const QString &imageData) | ||||
| 	: QThread() | ||||
| 	, _hyperion(hyperion) | ||||
| 	, _priority(priority) | ||||
| @@ -33,6 +33,7 @@ Effect::Effect(Hyperion* hyperion, int priority, int timeout, const QString & sc | ||||
| 	, _script(script) | ||||
| 	, _name(name) | ||||
| 	, _args(args) | ||||
| 	, _imageData(imageData) | ||||
| 	, _endTime(-1) | ||||
| 	, _colors() | ||||
| 	, _imageSize(hyperion->getLedGridSize()) | ||||
|   | ||||
| @@ -136,14 +136,14 @@ int EffectEngine::runEffect(const QString &effectName, int priority, int timeout | ||||
| 	return runEffect(effectName, QJsonObject(), priority, timeout, "", origin); | ||||
| } | ||||
|  | ||||
| int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args, int priority, int timeout, const QString &pythonScript, const QString &origin, unsigned smoothCfg) | ||||
| int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args, int priority, int timeout, const QString &pythonScript, const QString &origin, unsigned smoothCfg, const QString &imageData) | ||||
| { | ||||
| 	Info( _log, "run effect %s on channel %d", QSTRING_CSTR(effectName), priority); | ||||
|  | ||||
| 	if (pythonScript.isEmpty()) | ||||
| 	{ | ||||
| 		const EffectDefinition * effectDefinition = nullptr; | ||||
| 		for (const EffectDefinition & e : _availableEffects) | ||||
| 		const EffectDefinition *effectDefinition = nullptr; | ||||
| 		for (const EffectDefinition &e : _availableEffects) | ||||
| 		{ | ||||
| 			if (e.name == effectName) | ||||
| 			{ | ||||
| @@ -160,16 +160,16 @@ int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args, | ||||
|  | ||||
| 		return runEffectScript(effectDefinition->script, effectName, (args.isEmpty() ? effectDefinition->args : args), priority, timeout, origin, effectDefinition->smoothCfg); | ||||
| 	} | ||||
| 	return runEffectScript(pythonScript, effectName, args, priority, timeout, origin, smoothCfg); | ||||
| 	return runEffectScript(pythonScript, effectName, args, priority, timeout, origin, smoothCfg, imageData); | ||||
| } | ||||
|  | ||||
| int EffectEngine::runEffectScript(const QString &script, const QString &name, const QJsonObject &args, int priority, int timeout, const QString & origin, unsigned smoothCfg) | ||||
| int EffectEngine::runEffectScript(const QString &script, const QString &name, const QJsonObject &args, int priority, int timeout, const QString &origin, unsigned smoothCfg, const QString &imageData) | ||||
| { | ||||
| 	// clear current effect on the channel | ||||
| 	channelCleared(priority); | ||||
|  | ||||
| 	// create the effect | ||||
|     Effect * effect = new Effect(_hyperion, priority, timeout, script, name, args); | ||||
|     Effect *effect = new Effect(_hyperion, priority, timeout, script, name, args, imageData); | ||||
| 	connect(effect, &Effect::setInput, _hyperion, &Hyperion::setInput, Qt::QueuedConnection); | ||||
| 	connect(effect, &Effect::setInputImage, _hyperion, &Hyperion::setInputImage, Qt::QueuedConnection); | ||||
| 	connect(effect, &QThread::finished, this, &EffectEngine::effectFinished); | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #include <QFileInfo> | ||||
| #include <QDir> | ||||
| #include <QMap> | ||||
| #include <QByteArray> | ||||
|  | ||||
| // createEffect helper | ||||
| struct find_schema: std::unary_function<EffectSchema, bool> | ||||
| @@ -69,7 +70,15 @@ const bool EffectFileHandler::deleteEffect(const QString& effectName, QString& r | ||||
| 		{ | ||||
| 			if (effectConfigurationFile.exists()) | ||||
| 			{ | ||||
| 				if ( (it->script == ":/effects/gif.py") && !it->args.value("image").toString("").isEmpty()) | ||||
| 				{ | ||||
| 					QFileInfo effectImageFile(effectConfigurationFile.absolutePath() + "/" + it->args.value("image").toString()); | ||||
| 						if (effectImageFile.exists()) | ||||
| 							QFile::remove(effectImageFile.absoluteFilePath()); | ||||
| 				} | ||||
|  | ||||
| 				bool result = QFile::remove(effectConfigurationFile.absoluteFilePath()); | ||||
|  | ||||
| 				if (result) | ||||
| 				{ | ||||
| 					updateEffects(); | ||||
| @@ -141,6 +150,17 @@ const bool EffectFileHandler::saveEffect(const QJsonObject& message, QString& re | ||||
| 					newFileName.setFile(f); | ||||
| 				} | ||||
|  | ||||
| 				//TODO check if filename exist | ||||
| 				if (!message["imageData"].toString("").isEmpty() && !message["args"].toObject().value("image").toString("").isEmpty()) | ||||
| 				{ | ||||
| 					QFileInfo imageFileName(effectArray[0].toString().replace("$ROOT",_rootPath) + "/" + message["args"].toObject().value("image").toString()); | ||||
| 					if(!FileUtils::writeFile(imageFileName.absoluteFilePath(), QByteArray::fromBase64(message["imageData"].toString("").toUtf8()), _log)) | ||||
| 					{ | ||||
| 						resultMsg = "Error while saving image file '" + message["args"].toObject().value("image").toString() + ", please check the Hyperion Log"; | ||||
| 						return false; | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				if(!JsonUtils::write(newFileName.absoluteFilePath(), effectJson, _log)) | ||||
| 				{ | ||||
| 					resultMsg = "Error while saving effect, please check the Hyperion Log"; | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
| #include <QJsonArray> | ||||
| #include <QDateTime> | ||||
| #include <QImageReader> | ||||
| #include <QBuffer> | ||||
|  | ||||
| // create the hyperion module | ||||
| struct PyModuleDef EffectModule::moduleDef = { | ||||
| @@ -257,25 +258,42 @@ PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args) | ||||
|  | ||||
| PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args) | ||||
| { | ||||
| 	Q_INIT_RESOURCE(EffectEngine); | ||||
| 	Effect *effect = getEffect(); | ||||
|  | ||||
| 	char *source; | ||||
| 	if(!PyArg_ParseTuple(args, "s", &source)) | ||||
| 	QString file; | ||||
| 	QBuffer buffer; | ||||
| 	QImageReader reader; | ||||
|  | ||||
| 	if (effect->_imageData.isEmpty()) | ||||
| 	{ | ||||
| 		PyErr_SetString(PyExc_TypeError, "String required"); | ||||
| 		return NULL; | ||||
| 		Q_INIT_RESOURCE(EffectEngine); | ||||
|  | ||||
| 		char *source; | ||||
| 		if(!PyArg_ParseTuple(args, "s", &source)) | ||||
| 		{ | ||||
| 			PyErr_SetString(PyExc_TypeError, "String required"); | ||||
| 			return NULL; | ||||
| 		} | ||||
|  | ||||
| 		file = QString::fromUtf8(source); | ||||
|  | ||||
| 		if (file.mid(0, 1)  == ":") | ||||
| 			file = ":/effects/"+file.mid(1); | ||||
|  | ||||
| 		reader.setDecideFormatFromContent(true); | ||||
| 		reader.setFileName(file); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		buffer.setData(QByteArray::fromBase64(effect->_imageData.toUtf8())); | ||||
| 		buffer.open(QBuffer::ReadOnly); | ||||
| 		reader.setDecideFormatFromContent(true); | ||||
| 		reader.setDevice(&buffer); | ||||
| 	} | ||||
|  | ||||
| 	QString file = QString::fromUtf8(source); | ||||
|  | ||||
| 	if (file.mid(0, 1)  == ":") | ||||
| 		file = ":/effects/"+file.mid(1); | ||||
|  | ||||
| 	QImageReader reader(file); | ||||
|  | ||||
| 	if (reader.canRead()) | ||||
| 	{ | ||||
| 		PyObject* result = PyList_New(reader.imageCount()); | ||||
| 		PyObject *result = PyList_New(reader.imageCount()); | ||||
|  | ||||
| 		for (int i = 0; i < reader.imageCount(); ++i) | ||||
| 		{ | ||||
| @@ -290,7 +308,7 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args) | ||||
| 				QByteArray binaryImage; | ||||
| 				for (int i = 0; i<height; ++i) | ||||
| 				{ | ||||
| 					const QRgb * scanline = reinterpret_cast<const QRgb *>(qimage.scanLine(i)); | ||||
| 					const QRgb *scanline = reinterpret_cast<const QRgb *>(qimage.scanLine(i)); | ||||
| 					for (int j = 0; j< width; ++j) | ||||
| 					{ | ||||
| 						binaryImage.append((char) qRed(scanline[j])); | ||||
|   | ||||
| @@ -21,3 +21,7 @@ endif (ENABLE_V4L2) | ||||
| if (ENABLE_X11) | ||||
| 	add_subdirectory(x11) | ||||
| endif() | ||||
|  | ||||
| if (ENABLE_QT) | ||||
| 	add_subdirectory(qt) | ||||
| endif() | ||||
|   | ||||
							
								
								
									
										16
									
								
								libsrc/grabber/qt/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								libsrc/grabber/qt/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| # Define the current source locations | ||||
| SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) | ||||
| SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/qt) | ||||
|  | ||||
| find_package(Qt5Widgets REQUIRED) | ||||
|  | ||||
| include_directories( ${X11_INCLUDES} ) | ||||
|  | ||||
| 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} ) | ||||
|  | ||||
| target_link_libraries(qt-grabber | ||||
| 	hyperion | ||||
| 	Qt5::Widgets | ||||
| ) | ||||
							
								
								
									
										199
									
								
								libsrc/grabber/qt/QtGrabber.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								libsrc/grabber/qt/QtGrabber.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,199 @@ | ||||
| // proj | ||||
| #include <grabber/QtGrabber.h> | ||||
|  | ||||
| // qt | ||||
| #include <QPixmap> | ||||
| #include <QWindow> | ||||
| #include <QGuiApplication> | ||||
| #include <QWidget> | ||||
| #include <QScreen> | ||||
|  | ||||
| QtGrabber::QtGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display) | ||||
| 	: Grabber("QTGRABBER", 0, 0, cropLeft, cropRight, cropTop, cropBottom) | ||||
| 	, _display(unsigned(display)) | ||||
| 	, _pixelDecimation(pixelDecimation) | ||||
| 	, _screenWidth(0) | ||||
| 	, _screenHeight(0) | ||||
| 	, _src_x(0) | ||||
| 	, _src_y(0) | ||||
| 	, _src_x_max(0) | ||||
| 	, _src_y_max(0) | ||||
| 	, _screen(nullptr) | ||||
| { | ||||
| 	_useImageResampler = false; | ||||
|  | ||||
| 	// init | ||||
| 	setupDisplay(); | ||||
| } | ||||
|  | ||||
| QtGrabber::~QtGrabber() | ||||
| { | ||||
| 	freeResources(); | ||||
| } | ||||
|  | ||||
| void QtGrabber::freeResources() | ||||
| { | ||||
| 	// cleanup | ||||
| 	if (_screen != nullptr) | ||||
| 	{ | ||||
| 		delete _screen; | ||||
| 		_screen = nullptr; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| const bool QtGrabber::setupDisplay() | ||||
| { | ||||
| 	// cleanup last screen | ||||
| 	freeResources(); | ||||
|  | ||||
| 	QScreen* primary = QGuiApplication::primaryScreen(); | ||||
| 	QList<QScreen *> screens = QGuiApplication::screens(); | ||||
| 	// inject main screen at 0, if not nullptr | ||||
| 	if(primary != nullptr) | ||||
| 	{ | ||||
| 		screens.prepend(primary); | ||||
| 		// remove last main screen if twice in list | ||||
| 		if(screens.lastIndexOf(primary) > 0) | ||||
| 			screens.removeAt(screens.lastIndexOf(primary)); | ||||
| 	} | ||||
|  | ||||
| 	if(screens.isEmpty()) | ||||
| 	{ | ||||
| 		Error(_log, "No displays found to capture from!"); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	Info(_log,"Available Displays:"); | ||||
| 	int index = 0; | ||||
| 	for(auto screen : screens) | ||||
| 	{ | ||||
| 		const QRect geo = screen->geometry(); | ||||
| 		Info(_log,"Display %d: Name:%s Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", index, QSTRING_CSTR(screen->name()), geo.left(), geo.top() ,geo.right(), geo.bottom(), screen->depth()); | ||||
| 		index++; | ||||
| 	} | ||||
|  | ||||
| 	// be sure the index is available | ||||
| 	if(_display > unsigned(screens.size()-1)) | ||||
| 	{ | ||||
| 		Info(_log, "The requested display index '%d' is not available, falling back to display 0", _display); | ||||
| 		_display = 0; | ||||
| 	} | ||||
|  | ||||
| 	// init the requested display | ||||
| 	_screen = screens.at(_display); | ||||
| 	connect(_screen, &QScreen::geometryChanged, this, &QtGrabber::geometryChanged); | ||||
| 	updateScreenDimensions(true); | ||||
|  | ||||
| 	Info(_log,"Initialized display %d", _display); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void QtGrabber::geometryChanged(const QRect &geo) | ||||
| { | ||||
| 	Info(_log, "The current display changed geometry to (L,T,R,B) %d,%d,%d,%d", geo.left(), geo.top() ,geo.right(), geo.bottom()); | ||||
| 	updateScreenDimensions(true); | ||||
| } | ||||
|  | ||||
| int QtGrabber::grabFrame(Image<ColorRgb> & image) | ||||
| { | ||||
| 	if(_screen == nullptr) | ||||
| 	{ | ||||
| 		// reinit, this will disable capture on failure | ||||
| 		setEnabled(setupDisplay()); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max); | ||||
| 	QPixmap resizedPixmap = originalPixmap.scaled(_width,_height); | ||||
| 	QImage img = resizedPixmap.toImage().convertToFormat( QImage::Format_RGB888); | ||||
| 	memcpy(image.memptr(), img.bits(),_width*_height*3); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int QtGrabber::updateScreenDimensions(const bool& force) | ||||
| { | ||||
| 	if(!_screen) | ||||
| 		return -1; | ||||
|  | ||||
| 	const QRect& geo = _screen->geometry(); | ||||
| 	if (!force && _screenWidth == unsigned(geo.right()) && _screenHeight == unsigned(geo.bottom())) | ||||
| 	{ | ||||
| 		// No update required | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _screenWidth, _screenHeight, geo.right(), geo.bottom()); | ||||
| 	_screenWidth  = geo.right() - geo.left(); | ||||
| 	_screenHeight = geo.bottom() - geo.top(); | ||||
|  | ||||
| 	int width=0, height=0; | ||||
|  | ||||
| 	// Image scaling is performed by Qt | ||||
| 	width  =  (_screenWidth > unsigned(_cropLeft + _cropRight)) | ||||
| 		? ((_screenWidth - _cropLeft - _cropRight) / _pixelDecimation) | ||||
| 		: (_screenWidth / _pixelDecimation); | ||||
|  | ||||
| 	height =  (_screenHeight > unsigned(_cropTop + _cropBottom)) | ||||
| 		? ((_screenHeight - _cropTop - _cropBottom) / _pixelDecimation) | ||||
| 		: (_screenHeight / _pixelDecimation); | ||||
|  | ||||
|  | ||||
| 	// calculate final image dimensions and adjust top/left cropping in 3D modes | ||||
| 	switch (_videoMode) | ||||
| 	{ | ||||
| 	case VIDEO_3DSBS: | ||||
| 		_width  = width /2; | ||||
| 		_height = height; | ||||
| 		_src_x  = _cropLeft / 2; | ||||
| 		_src_y  = _cropTop; | ||||
| 		_src_x_max = (_screenWidth / 2) - _cropRight; | ||||
| 		_src_y_max = _screenHeight - _cropBottom; | ||||
| 		break; | ||||
| 	case VIDEO_3DTAB: | ||||
| 		_width  = width; | ||||
| 		_height = height / 2; | ||||
| 		_src_x  = _cropLeft; | ||||
| 		_src_y  = _cropTop / 2; | ||||
| 		_src_x_max = _screenWidth - _cropRight; | ||||
| 		_src_y_max = (_screenHeight / 2) - _cropBottom; | ||||
| 		break; | ||||
| 	case VIDEO_2D: | ||||
| 	default: | ||||
| 		_width  = width; | ||||
| 		_height = height; | ||||
| 		_src_x  = _cropLeft; | ||||
| 		_src_y  = _cropTop; | ||||
| 		_src_x_max = _screenWidth - _cropRight; | ||||
| 		_src_y_max = _screenHeight - _cropBottom; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	Info(_log, "Update output image resolution to [%dx%d]", _width, _height); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void QtGrabber::setVideoMode(VideoMode mode) | ||||
| { | ||||
| 	Grabber::setVideoMode(mode); | ||||
| 	updateScreenDimensions(true); | ||||
| } | ||||
|  | ||||
| void QtGrabber::setPixelDecimation(int pixelDecimation) | ||||
| { | ||||
| 	_pixelDecimation = pixelDecimation; | ||||
| } | ||||
|  | ||||
| void QtGrabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom) | ||||
| { | ||||
| 	Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom); | ||||
| 	updateScreenDimensions(true); | ||||
| } | ||||
|  | ||||
| void QtGrabber::setDisplayIndex(int index) | ||||
| { | ||||
| 	if(_display != unsigned(index)) | ||||
| 	{ | ||||
| 		_display = unsigned(index); | ||||
| 		setupDisplay(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										11
									
								
								libsrc/grabber/qt/QtWrapper.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								libsrc/grabber/qt/QtWrapper.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #include <grabber/QtWrapper.h> | ||||
|  | ||||
| QtWrapper::QtWrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display, const unsigned updateRate_Hz) | ||||
| 	: GrabberWrapper("Qt", &_grabber, 0, 0, updateRate_Hz) | ||||
| 	, _grabber(cropLeft, cropRight, cropTop, cropBottom, pixelDecimation, display) | ||||
| {} | ||||
|  | ||||
| void QtWrapper::action() | ||||
| { | ||||
| 	transferFrame(_grabber); | ||||
| } | ||||
| @@ -77,6 +77,10 @@ QStringList GrabberWrapper::availableGrabbers() | ||||
| 	grabbers << "x11"; | ||||
| 	#endif | ||||
|  | ||||
| 	#ifdef ENABLE_QT | ||||
| 	grabbers << "qt"; | ||||
| 	#endif | ||||
|  | ||||
| 	return grabbers; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -529,9 +529,9 @@ int Hyperion::setEffect(const QString &effectName, int priority, int timeout, co | ||||
| 	return _effectEngine->runEffect(effectName, priority, timeout, origin); | ||||
| } | ||||
|  | ||||
| int Hyperion::setEffect(const QString &effectName, const QJsonObject &args, int priority, int timeout, const QString & pythonScript, const QString & origin) | ||||
| int Hyperion::setEffect(const QString &effectName, const QJsonObject &args, int priority, int timeout, const QString &pythonScript, const QString &origin, const QString &imageData) | ||||
| { | ||||
| 	return _effectEngine->runEffect(effectName, args, priority, timeout, pythonScript, origin); | ||||
| 	return _effectEngine->runEffect(effectName, args, priority, timeout, pythonScript, origin, 0, imageData); | ||||
| } | ||||
|  | ||||
| void Hyperion::setLedMappingType(const int& mappingType) | ||||
|   | ||||
| @@ -47,19 +47,24 @@ ImageToLedsMap::ImageToLedsMap( | ||||
| 		minX_idx = qMin(minX_idx, xOffset + actualWidth - 1); | ||||
| 		if (minX_idx == maxX_idx) | ||||
| 		{ | ||||
| 			maxX_idx = minX_idx + 1; | ||||
| 			maxX_idx++; | ||||
| 		} | ||||
| 		minY_idx = qMin(minY_idx, yOffset + actualHeight - 1); | ||||
| 		if (minY_idx == maxY_idx) | ||||
| 		{ | ||||
| 			maxY_idx = minY_idx + 1; | ||||
| 			maxY_idx++; | ||||
| 		} | ||||
|  | ||||
| 		// Add all the indices in the above defined rectangle to the indices for this led | ||||
| 		const auto maxYLedCount = qMin(maxY_idx, yOffset+actualHeight); | ||||
| 		const auto maxXLedCount = qMin(maxX_idx, xOffset+actualWidth); | ||||
|  | ||||
| 		std::vector<unsigned> ledColors; | ||||
| 		for (unsigned y = minY_idx; y<maxY_idx && y<(yOffset+actualHeight); ++y) | ||||
| 		ledColors.reserve(maxXLedCount*maxYLedCount); | ||||
|  | ||||
| 		for (unsigned y = minY_idx; y < maxYLedCount; ++y) | ||||
| 		{ | ||||
| 			for (unsigned x = minX_idx; x<maxX_idx && x<(xOffset+actualWidth); ++x) | ||||
| 			for (unsigned x = minX_idx; x < maxXLedCount; ++x) | ||||
| 			{ | ||||
| 				ledColors.push_back(y*width + x); | ||||
| 			} | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| 		{ | ||||
| 			"type" : "string", | ||||
| 			"title" : "edt_conf_fg_type_title", | ||||
| 			"enum"  : ["auto","dispmanx","amlogic","x11","framebuffer"], | ||||
| 			"enum"  : ["auto","dispmanx","amlogic","x11","framebuffer","qt"], | ||||
| 			"default" : "auto", | ||||
| 			"propertyOrder" : 2 | ||||
| 		}, | ||||
|   | ||||
| @@ -144,8 +144,8 @@ void RgbTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue) | ||||
| { | ||||
| 	// apply gamma | ||||
| 	red   = _mappingR[red]; | ||||
| 	green = _mappingR[green]; | ||||
| 	blue  = _mappingR[blue]; | ||||
| 	green = _mappingG[green]; | ||||
| 	blue  = _mappingB[blue]; | ||||
|  | ||||
| 	// apply brightnesss | ||||
| 	int rgbSum = red+green+blue; | ||||
|   | ||||
| @@ -23,6 +23,8 @@ QtHttpClientWrapper::QtHttpClientWrapper (QTcpSocket * sock, QtHttpServer * pare | ||||
|     , m_sockClient     (sock) | ||||
|     , m_currentRequest (Q_NULLPTR) | ||||
|     , m_serverHandle   (parent) | ||||
|     , m_websocketClient(nullptr) | ||||
|     , m_webJsonRpc     (nullptr) | ||||
| { | ||||
|     connect (m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived); | ||||
| } | ||||
|   | ||||
| @@ -50,8 +50,8 @@ private: | ||||
|     QTcpSocket    *   m_sockClient; | ||||
|     QtHttpRequest *   m_currentRequest; | ||||
|     QtHttpServer  *   m_serverHandle; | ||||
| 	WebSocketClient * m_websocketClient = nullptr; | ||||
| 	WebJsonRpc *      m_webJsonRpc = nullptr; | ||||
| 	WebSocketClient * m_websocketClient; | ||||
| 	WebJsonRpc *      m_webJsonRpc; | ||||
| }; | ||||
|  | ||||
| #endif // QTHTTPCLIENTWRAPPER_H | ||||
|   | ||||
							
								
								
									
										42
									
								
								src/hyperion-qt/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/hyperion-qt/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| cmake_minimum_required(VERSION 3.0.0) | ||||
| project(hyperion-qt) | ||||
|  | ||||
| find_package(Qt5Widgets 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 | ||||
| ) | ||||
|  | ||||
| add_executable(${PROJECT_NAME} | ||||
| 	${Hyperion_QT_HEADERS} | ||||
| 	${Hyperion_QT_SOURCES} | ||||
| ) | ||||
|  | ||||
| target_link_libraries(${PROJECT_NAME} | ||||
| 	commandline | ||||
| 	qt-grabber | ||||
| 	flatbufserver | ||||
| 	flatbuffers | ||||
| 	ssdp | ||||
| 	Qt5::Core | ||||
| 	Qt5::Widgets | ||||
| 	Qt5::Network | ||||
| ) | ||||
|  | ||||
| install ( TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin/" COMPONENT "${PLATFORM}" ) | ||||
|  | ||||
| if(CMAKE_HOST_UNIX) | ||||
| 	install(CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\" )" COMPONENT  "${PLATFORM}" ) | ||||
| 	install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "${PLATFORM}" ) | ||||
| 	install(CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME} )" COMPONENT  "${PLATFORM}" ) | ||||
| endif(CMAKE_HOST_UNIX) | ||||
							
								
								
									
										42
									
								
								src/hyperion-qt/QtWrapper.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/hyperion-qt/QtWrapper.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
|  | ||||
| // Hyperion-qt includes | ||||
| #include "QtWrapper.h" | ||||
|  | ||||
| QtWrapper::QtWrapper(int grabInterval, int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display) : | ||||
| 	_timer(this), | ||||
| 	_grabber(cropLeft, cropRight, cropTop, cropBottom, pixelDecimation, display) | ||||
| { | ||||
| 	_timer.setInterval(grabInterval); | ||||
| 	// Connect capturing to the timeout signal of the timer | ||||
| 	connect(&_timer, SIGNAL(timeout()), this, SLOT(capture())); | ||||
| } | ||||
|  | ||||
| const Image<ColorRgb> & QtWrapper::getScreenshot() | ||||
| { | ||||
| 	_grabber.grabFrame(_screenshot); | ||||
| 	return _screenshot; | ||||
| } | ||||
|  | ||||
| void QtWrapper::start() | ||||
| { | ||||
| 	_timer.start(); | ||||
| } | ||||
|  | ||||
| void QtWrapper::stop() | ||||
| { | ||||
| 	_timer.stop(); | ||||
| } | ||||
|  | ||||
| void QtWrapper::capture() | ||||
| { | ||||
| 	if(unsigned(_grabber.getImageWidth()) != unsigned(_screenshot.width()) || unsigned(_grabber.getImageHeight()) != unsigned(_screenshot.height())) | ||||
| 		_screenshot.resize(_grabber.getImageWidth(),_grabber.getImageHeight()); | ||||
|  | ||||
| 	_grabber.grabFrame(_screenshot); | ||||
| 	emit sig_screenshot(_screenshot); | ||||
| } | ||||
|  | ||||
| void QtWrapper::setVideoMode(const VideoMode mode) | ||||
| { | ||||
| 	_grabber.setVideoMode(mode); | ||||
| } | ||||
							
								
								
									
										51
									
								
								src/hyperion-qt/QtWrapper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/hyperion-qt/QtWrapper.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
|  | ||||
| // QT includes | ||||
| #include <QTimer> | ||||
|  | ||||
| // Hyperion-Qt includes | ||||
| #include <grabber/QtGrabber.h> | ||||
|  | ||||
| //Utils includes | ||||
| #include <utils/VideoMode.h> | ||||
|  | ||||
| class QtWrapper : public QObject | ||||
| { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
| 	QtWrapper(int grabInterval, int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, int display); | ||||
|  | ||||
| 	const Image<ColorRgb> & getScreenshot(); | ||||
|  | ||||
| 	/// | ||||
| 	/// Starts the timed capturing of screenshots | ||||
| 	/// | ||||
| 	void start(); | ||||
|  | ||||
| 	void stop(); | ||||
|  | ||||
| signals: | ||||
| 	void sig_screenshot(const Image<ColorRgb> & screenshot); | ||||
|  | ||||
| public slots: | ||||
| 	/// | ||||
| 	/// Set the video mode (2D/3D) | ||||
| 	/// @param[in] mode The new video mode | ||||
| 	/// | ||||
| 	void setVideoMode(const VideoMode videoMode); | ||||
|  | ||||
| private slots: | ||||
| 	/// | ||||
| 	/// Performs a single screenshot capture and publishes the capture screenshot on the screenshot | ||||
| 	/// signal. | ||||
| 	/// | ||||
| 	void capture(); | ||||
|  | ||||
| private: | ||||
| 	/// The QT timer to generate capture-publish events | ||||
| 	QTimer _timer; | ||||
|  | ||||
| 	/// The grabber for creating screenshots | ||||
| 	QtGrabber _grabber; | ||||
|  | ||||
| 	Image<ColorRgb>  _screenshot; | ||||
| }; | ||||
							
								
								
									
										111
									
								
								src/hyperion-qt/hyperion-qt.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/hyperion-qt/hyperion-qt.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
|  | ||||
| // QT includes | ||||
| #include <QCoreApplication> | ||||
| #include <QImage> | ||||
|  | ||||
| #include "QtWrapper.h" | ||||
| #include <utils/ColorRgb.h> | ||||
| #include <utils/Image.h> | ||||
| #include <commandline/Parser.h> | ||||
|  | ||||
| //flatbuf sending | ||||
| #include <flatbufserver/FlatBufferConnection.h> | ||||
|  | ||||
| // ssdp discover | ||||
| #include <ssdp/SSDPDiscover.h> | ||||
|  | ||||
| using namespace commandline; | ||||
|  | ||||
| // save the image as screenshot | ||||
| void saveScreenshot(QString filename, const Image<ColorRgb> & image) | ||||
| { | ||||
| 	// store as PNG | ||||
| 	QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); | ||||
| 	pngImage.save(filename); | ||||
| } | ||||
|  | ||||
| int main(int argc, char ** argv) | ||||
| { | ||||
| 	//QCoreApplication app(argc, argv); | ||||
| 	QGuiApplication app(argc, argv); | ||||
|  | ||||
| 	try | ||||
| 	{ | ||||
| 		// create the option parser and initialize all parameters | ||||
| 		Parser parser("Qt interface capture application for Hyperion"); | ||||
|  | ||||
| 		Option        & argDisplay         = parser.add<Option>       ('d', "display",    "Set the display to capture [default: %1]","0"); | ||||
| 		IntOption     & argFps             = parser.add<IntOption>    ('f', "framerate",  "Capture frame rate [default: %1]", "10", 1, 25); | ||||
| 		IntOption     & argCropLeft        = parser.add<IntOption>    (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)"); | ||||
| 		IntOption     & argCropRight       = parser.add<IntOption>    (0x0, "crop-right", "Number of pixels to crop from the right of the picture before decimation (overrides --crop-width)"); | ||||
| 		IntOption     & argCropTop         = parser.add<IntOption>    (0x0, "crop-top", "Number of pixels to crop from the top of the picture before decimation (overrides --crop-height)"); | ||||
| 		IntOption     & argCropBottom      = parser.add<IntOption>    (0x0, "crop-bottom", "Number of pixels to crop from the bottom of the picture before decimation (overrides --crop-height)"); | ||||
| 		IntOption     & argSizeDecimation  = parser.add<IntOption>    ('s', "size-decimator", "Decimation factor for the output image size [default=%1]", "8", 1); | ||||
| 		BooleanOption & argScreenshot      = parser.add<BooleanOption>(0x0, "screenshot",   "Take a single screenshot, save it to file and quit"); | ||||
| 		Option        & argAddress         = parser.add<Option>       ('a', "address",    "Set the address of the hyperion server [default: %1]", "127.0.0.1:19445"); | ||||
| 		IntOption     & argPriority        = parser.add<IntOption>    ('p', "priority",   "Use the provided priority channel (suggested 100-199) [default: %1]", "150"); | ||||
| 		BooleanOption & argSkipReply       = parser.add<BooleanOption>(0x0, "skip-reply", "Do not receive and check reply messages from Hyperion"); | ||||
| 		BooleanOption & argHelp            = parser.add<BooleanOption>('h', "help",        "Show this help message and exit"); | ||||
|  | ||||
| 		// parse all arguments | ||||
| 		parser.process(app); | ||||
|  | ||||
| 		// check if we need to display the usage. exit if we do. | ||||
| 		if (parser.isSet(argHelp)) | ||||
| 		{ | ||||
| 			parser.showHelp(0); | ||||
| 		} | ||||
|  | ||||
| 		QtWrapper grabber( | ||||
| 			1000 / argFps.getInt(parser), | ||||
| 			argCropLeft.getInt(parser), | ||||
| 			argCropRight.getInt(parser), | ||||
| 			argCropTop.getInt(parser), | ||||
| 			argCropBottom.getInt(parser), | ||||
| 			argSizeDecimation.getInt(parser), | ||||
| 			parser.isSet(argDisplay)); | ||||
|  | ||||
| 		if (parser.isSet(argScreenshot)) | ||||
| 		{ | ||||
| 			// Capture a single screenshot and finish | ||||
| 			const Image<ColorRgb> &screenshot = grabber.getScreenshot(); | ||||
| 			saveScreenshot("screenshot.png", screenshot); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			// server searching by ssdp | ||||
| 			QString address; | ||||
| 			if(parser.isSet(argAddress)) | ||||
| 			{ | ||||
| 				address = argAddress.value(parser); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				SSDPDiscover discover; | ||||
| 				address = discover.getFirstService(STY_FLATBUFSERVER); | ||||
| 				if(address.isEmpty()) | ||||
| 				{ | ||||
| 					address = argAddress.value(parser); | ||||
| 				} | ||||
| 			} | ||||
| 			// Create the Flabuf-connection | ||||
| 			FlatBufferConnection flatbuf("Qt Standalone", address, argPriority.getInt(parser), parser.isSet(argSkipReply)); | ||||
|  | ||||
| 			// Connect the screen capturing to flatbuf connection processing | ||||
| 			QObject::connect(&grabber, SIGNAL(sig_screenshot(const Image<ColorRgb> &)), &flatbuf, SLOT(setImage(Image<ColorRgb>))); | ||||
|  | ||||
| 			// Start the capturing | ||||
| 			grabber.start(); | ||||
|  | ||||
| 			// Start the application | ||||
| 			app.exec(); | ||||
| 		} | ||||
| 	} | ||||
| 	catch (const std::runtime_error & e) | ||||
| 	{ | ||||
| 		// An error occured. Display error and quit | ||||
| 		Error(Logger::getInstance("QTGRABBER"), "%s", e.what()); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| find_package(PythonLibs 3.5 REQUIRED) | ||||
| find_package(PythonLibs 3.4 REQUIRED) | ||||
| include_directories(${PYTHON_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}/..) | ||||
|  | ||||
| add_executable(hyperiond | ||||
| @@ -55,7 +55,9 @@ if (ENABLE_X11) | ||||
| 	target_link_libraries(hyperiond	x11-grabber	) | ||||
| endif () | ||||
|  | ||||
| qt5_use_modules(hyperiond Core Gui Network Widgets) | ||||
| if (ENABLE_QT) | ||||
| 	target_link_libraries(hyperiond	qt-grabber	) | ||||
| endif () | ||||
|  | ||||
| install ( TARGETS hyperiond DESTINATION "share/hyperion/bin/" COMPONENT "${PLATFORM}" ) | ||||
| install ( DIRECTORY ${CMAKE_SOURCE_DIR}/bin/service DESTINATION "share/hyperion/" COMPONENT  "${PLATFORM}" ) | ||||
|   | ||||
| @@ -60,6 +60,7 @@ HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObje | ||||
| 	, _amlGrabber(nullptr) | ||||
| 	, _fbGrabber(nullptr) | ||||
| 	, _osxGrabber(nullptr) | ||||
| 	, _qtGrabber(nullptr) | ||||
| 	, _hyperion(nullptr) | ||||
| 	, _stats(nullptr) | ||||
| 	, _ssdp(nullptr) | ||||
| @@ -155,7 +156,8 @@ void HyperionDaemon::freeObjects() | ||||
| 	delete _dispmanx; | ||||
| 	delete _fbGrabber; | ||||
| 	delete _osxGrabber; | ||||
| 	 | ||||
| 	delete _qtGrabber; | ||||
|  | ||||
| 	for(V4L2Wrapper* grabber : _v4l2Grabbers) | ||||
| 	{ | ||||
| 		delete grabber; | ||||
| @@ -168,6 +170,7 @@ void HyperionDaemon::freeObjects() | ||||
| 	_dispmanx       = nullptr; | ||||
| 	_fbGrabber      = nullptr; | ||||
| 	_osxGrabber     = nullptr; | ||||
| 	_qtGrabber      = nullptr; | ||||
| 	_webserver      = nullptr; | ||||
| 	_jsonServer     = nullptr; | ||||
| 	_udpListener    = nullptr; | ||||
| @@ -269,10 +272,10 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type& type, const QJso | ||||
| 			{ | ||||
| 				type = "x11"; | ||||
| 			} | ||||
| 			// framebuffer -> if nothing other applies | ||||
| 			// qt -> if nothing other applies | ||||
| 			else | ||||
| 			{ | ||||
| 				type = "framebuffer"; | ||||
| 				type = "qt"; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @@ -296,6 +299,9 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type& type, const QJso | ||||
| 			#ifdef ENABLE_X11 | ||||
| 			if(_x11Grabber != nullptr) _x11Grabber->stop(); | ||||
| 			#endif | ||||
| 			#ifdef ENABLE_QT | ||||
| 			if(_qtGrabber != nullptr) _qtGrabber->stop(); | ||||
| 			#endif | ||||
|  | ||||
| 			// create/start capture interface | ||||
| 			if(type == "framebuffer") | ||||
| @@ -338,6 +344,19 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type& type, const QJso | ||||
| 				_x11Grabber->start(); | ||||
| 				#endif | ||||
| 			} | ||||
| 			else if(type == "qt") | ||||
| 			{ | ||||
| 				if(_qtGrabber == nullptr) | ||||
| 					createGrabberQt(grabberConfig); | ||||
| 				#ifdef ENABLE_QT | ||||
| 				_qtGrabber->start(); | ||||
| 				#endif | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				Error(_log,"Unknown platform capture type: %s", QSTRING_CSTR(type)); | ||||
| 				return; | ||||
| 			} | ||||
| 			_prevType = type; | ||||
| 		} | ||||
| 	} | ||||
| @@ -446,6 +465,24 @@ void HyperionDaemon::createGrabberX11(const QJsonObject & grabberConfig) | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void HyperionDaemon::createGrabberQt(const QJsonObject & grabberConfig) | ||||
| { | ||||
| #ifdef ENABLE_QT | ||||
| 	_qtGrabber = new QtWrapper( | ||||
| 				_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom, | ||||
| 				grabberConfig["pixelDecimation"].toInt(8), | ||||
| 				grabberConfig["display"].toInt(0), | ||||
| 				_grabber_frequency ); | ||||
|  | ||||
| 	// connect to HyperionDaemon signal | ||||
| 	connect(this, &HyperionDaemon::videoMode, _qtGrabber, &QtWrapper::setVideoMode); | ||||
| 	connect(this, &HyperionDaemon::settingsChanged, _qtGrabber, &QtWrapper::handleSettingsUpdate); | ||||
|  | ||||
| 	Info(_log, "Qt grabber created"); | ||||
| #else | ||||
| 	Error(_log, "The Qt grabber can not be instantiated, because it has been left out from the build"); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void HyperionDaemon::createGrabberFramebuffer(const QJsonObject & grabberConfig) | ||||
| { | ||||
|   | ||||
| @@ -39,6 +39,12 @@ | ||||
| 	typedef QObject X11Wrapper; | ||||
| #endif | ||||
|  | ||||
| #ifdef ENABLE_QT | ||||
| 	#include <grabber/QtWrapper.h> | ||||
| #else | ||||
| 	typedef QObject QtWrapper; | ||||
| #endif | ||||
|  | ||||
| #include <utils/Logger.h> | ||||
| #include <utils/Image.h> | ||||
| #include <utils/VideoMode.h> | ||||
| @@ -122,6 +128,7 @@ private: | ||||
| 	void createGrabberFramebuffer(const QJsonObject & grabberConfig); | ||||
| 	void createGrabberOsx(const QJsonObject & grabberConfig); | ||||
| 	void createGrabberX11(const QJsonObject & grabberConfig); | ||||
| 	void createGrabberQt(const QJsonObject & grabberConfig); | ||||
|  | ||||
| 	Logger*                _log; | ||||
| 	BonjourBrowserWrapper* _bonjourBrowserWrapper; | ||||
| @@ -135,6 +142,7 @@ private: | ||||
| 	AmlogicWrapper*        _amlGrabber; | ||||
| 	FramebufferWrapper*    _fbGrabber; | ||||
| 	OsxWrapper*            _osxGrabber; | ||||
| 	QtWrapper*             _qtGrabber; | ||||
| 	Hyperion*              _hyperion; | ||||
| 	Stats*                 _stats; | ||||
| 	SSDPHandler*           _ssdp; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user