mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
update
This commit is contained in:
parent
0e3ddb7eca
commit
d6b2cfaf9d
@ -276,6 +276,9 @@ if (ENABLE_TESTS)
|
||||
add_subdirectory(test)
|
||||
endif ()
|
||||
|
||||
# Add resources directory
|
||||
add_subdirectory(resources)
|
||||
|
||||
# Add the doxygen generation directory
|
||||
add_subdirectory(doc)
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,9 +1,10 @@
|
||||
|
||||
execute_process( COMMAND git log -1 --format=%cn-%t/%h-%ct WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE BUILD_ID ERROR_QUIET )
|
||||
execute_process( COMMAND sh -c "git branch | grep '^*' | sed 's;^*;;g' " WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE VERSION_ID ERROR_QUIET )
|
||||
execute_process( COMMAND sh -c "git remote --verbose | grep origin | grep fetch | cut -f2 | cut -d' ' -f1" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_REMOTE_PATH ERROR_QUIET )
|
||||
|
||||
STRING ( STRIP "${BUILD_ID}" BUILD_ID )
|
||||
STRING ( STRIP "${VERSION_ID}" VERSION_ID )
|
||||
SET ( HYPERION_BUILD_ID "${VERSION_ID} (${BUILD_ID})" )
|
||||
STRING ( STRIP "${GIT_REMOTE_PATH}" GIT_REMOTE_PATH )
|
||||
SET ( HYPERION_BUILD_ID "${VERSION_ID} (${BUILD_ID}) Git Remote: ${GIT_REMOTE_PATH}" )
|
||||
message ( STATUS "Current Version: ${HYPERION_BUILD_ID}" )
|
||||
|
||||
|
5
cmake/osxbundle/launch.sh
Normal file
5
cmake/osxbundle/launch.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
cd "$(dirname "$0")"
|
||||
# Path to hyperiond!?
|
||||
cd ../Resources/bin
|
||||
exec ./hyperiond "$@"
|
@ -1,38 +1,63 @@
|
||||
# cmake file for generating distribution packages
|
||||
|
||||
IF (APPLE)
|
||||
SET ( CPACK_GENERATOR "TGZ" "Bundle") # "RPM"
|
||||
ELSE()
|
||||
SET ( CPACK_GENERATOR "TGZ" "Bundle")
|
||||
ELSEIF (UNIX)
|
||||
SET ( CPACK_GENERATOR "DEB" "TGZ" "STGZ") # "RPM"
|
||||
ELSEIF (WIN32)
|
||||
SET ( CPACK_GENERATOR "ZIP" "NSIS")
|
||||
ENDIF()
|
||||
|
||||
SET ( CPACK_PACKAGE_NAME "hyperion" )
|
||||
# Apply to all packages, some of these can be overwritten with generator specific content
|
||||
# https://cmake.org/cmake/help/v3.0/module/CPack.html
|
||||
|
||||
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_CONTACT "packages@hyperion-project.org")
|
||||
SET ( CPACK_PACKAGE_EXECUTABLES "hyperiond;Hyperion" )
|
||||
SET ( CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/icons/hyperion-icon-32px.png")
|
||||
SET ( CPACK_PACKAGE_VERSION_MAJOR "${HYPERION_VERSION_MAJOR}")
|
||||
SET ( CPACK_PACKAGE_VERSION_MINOR "${HYPERION_VERSION_MINOR}")
|
||||
SET ( CPACK_PACKAGE_VERSION_PATCH "${HYPERION_VERSION_PATCH}")
|
||||
SET ( CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" )
|
||||
SET ( CPACK_CREATE_DESKTOP_LINKS "hyperiond;Hyperion" )
|
||||
|
||||
|
||||
# Specific CPack Package Generators
|
||||
# https://cmake.org/Wiki/CMake:CPackPackageGenerators
|
||||
# .deb files for dpkg
|
||||
|
||||
SET ( CPACK_DEBIAN_PACKAGE_MAINTAINER "Hyperion Team")
|
||||
SET ( CPACK_DEBIAN_PACKAGE_NAME "Hyperion" )
|
||||
SET ( CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/preinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/postinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/prerm" )
|
||||
SET ( CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://www.hyperion-project.org" )
|
||||
SET ( CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5core5a (>= 5.5.0), libqt5network5 (>= 5.5.0), libqt5gui5 (>= 5.5.0), libqt5serialport5 (>= 5.5.0), libqt5sql5 (>= 5.5.0), libavahi-core7 (>= 0.6.31), libavahi-compat-libdnssd1 (>= 0.6.31), libusb-1.0-0, libpython3.5, libc6" )
|
||||
SET ( CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5core5a (>= 5.5.0), libqt5network5 (>= 5.5.0), libqt5gui5 (>= 5.5.0), libqt5serialport5 (>= 5.5.0), libqt5sql5 (>= 5.5.0), libqt5sql5-sqlite (>= 5.5.0), libavahi-core7 (>= 0.6.31), libavahi-compat-libdnssd1 (>= 0.6.31), libusb-1.0-0, libpython3.5, libc6" )
|
||||
SET ( CPACK_DEBIAN_PACKAGE_SECTION "Miscellaneous" )
|
||||
|
||||
SET ( CPACK_RPM_PACKAGE_NAME "Hyperion" )
|
||||
SET ( CPACK_RPM_PACKAGE_URL "https://github.com/hyperion-project/hyperion.ng" )
|
||||
# .rpm for rpm
|
||||
SET ( CPACK_RPM_PACKAGE_RELEASE 1)
|
||||
SET ( CPACK_RPM_PACKAGE_LICENSE "unknown")
|
||||
SET ( CPACK_RPM_PACKAGE_GROUP "unknown")
|
||||
SET ( CPACK_RPM_PACKAGE_REQUIRES "libqt5core5a >= 5.5.0, libqt5network5 >= 5.5.0, libqt5gui5 >= 5.5.0, libqt5serialport5 >= 5.5.0, libqt5sql5 >= 5.5.0, libqt5sql5-sqlite >= 5.5.0, libavahi-core7 >= 0.6.31, libavahi-compat-libdnssd1 >= 0.6.31, libusb-1.0-0, libpython3.5, libc6")
|
||||
SET ( CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/rpm/postinst" )
|
||||
|
||||
SET ( CPACK_PACKAGE_FILE_NAME "Hyperion")
|
||||
SET ( CPACK_PACKAGE_ICON ${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Hyperion.icns )
|
||||
# OSX "Bundle" generator TODO Add more osx generators
|
||||
# https://cmake.org/cmake/help/v3.10/module/CPackBundle.html
|
||||
SET ( CPACK_BUNDLE_NAME "Hyperion" )
|
||||
SET ( CPACK_BUNDLE_ICON ${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Hyperion.icns )
|
||||
SET ( CPACK_BUNDLE_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Info.plist )
|
||||
#SET ( CPACK_BUNDLE_STARTUP_COMMAND - path to a file that will be executed when the user opens the bundle. Could be a shell-script or a binary. )
|
||||
SET ( CPACK_BUNDLE_ICON ${CMAKE_CURRENT_SOURCE_DIR}/cmake/osxbundle/Hyperion.icns )
|
||||
SET ( CPACK_BUNDLE_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/cmake/osxbundle/Info.plist )
|
||||
SET ( CPACK_BUNDLE_STARTUP_COMMAND "${CMAKE_SOURCE_DIR}/cmake/osxbundle/launch.sh" )
|
||||
|
||||
SET(CPACK_PACKAGE_VERSION_MAJOR "${HYPERION_VERSION_MAJOR}")
|
||||
SET(CPACK_PACKAGE_VERSION_MINOR "${HYPERION_VERSION_MINOR}")
|
||||
SET(CPACK_PACKAGE_VERSION_PATCH "${HYPERION_VERSION_PATCH}")
|
||||
# NSIS for windows, requires NSIS TODO finish
|
||||
SET ( CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/cmake/nsis/installer.ico")
|
||||
SET ( CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}/cmake/nsis/uninstaller.ico")
|
||||
#SET ( CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/cmake/nsis/installer.bmp") #bmp required? If so, wrap in WIN32 check else: Use default icon instead
|
||||
SET ( CPACK_NSIS_MODIFY_PATH ON)
|
||||
SET ( CPACK_NSIS_DISPLAY_NAME "Hyperion Installer")
|
||||
SET ( CPACK_NSIS_INSTALLED_ICON_NAME "Link to .exe")
|
||||
SET ( CPACK_NSIS_HELP_LINK "https://www.hyperion-project.org")
|
||||
SET ( CPACK_NSIS_URL_INFO_ABOUT "https://www.hyperion-project.org")
|
||||
|
||||
# define the install components
|
||||
SET ( CPACK_COMPONENTS_ALL "${PLATFORM}" )
|
||||
SET ( CPACK_ARCHIVE_COMPONENT_INSTALL ON )
|
||||
SET ( CPACK_DEB_COMPONENT_INSTALL ON )
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
#define _BSD_SOURCE // for usleep from unistd.h
|
||||
#define _DEFAULT_SOURCE // for usleep from unistd.h
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -7,33 +7,10 @@
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
// qt includess
|
||||
#include <QTimer>
|
||||
#include <QJsonObject>
|
||||
#include <QMutex>
|
||||
#include <QString>
|
||||
|
||||
// createEffect helper
|
||||
struct find_schema: std::unary_function<EffectSchema, bool>
|
||||
{
|
||||
QString pyFile;
|
||||
find_schema(QString pyFile):pyFile(pyFile) { }
|
||||
bool operator()(EffectSchema const& schema) const
|
||||
{
|
||||
return schema.pyFile == pyFile;
|
||||
}
|
||||
};
|
||||
|
||||
// deleteEffect helper
|
||||
struct find_effect: std::unary_function<EffectDefinition, bool>
|
||||
{
|
||||
QString effectName;
|
||||
find_effect(QString effectName) :effectName(effectName) { }
|
||||
bool operator()(EffectDefinition const& effectDefinition) const
|
||||
{
|
||||
return effectDefinition.name == effectName;
|
||||
}
|
||||
};
|
||||
|
||||
class JsonCB;
|
||||
|
||||
class JsonAPI : public QObject
|
||||
@ -59,8 +36,11 @@ public:
|
||||
void handleMessage(const QString & message);
|
||||
|
||||
public slots:
|
||||
/// _timer_ledcolors requests ledcolor updates (if enabled)
|
||||
void streamLedcolorsUpdate();
|
||||
///
|
||||
/// @brief is called whenever the current Hyperion instance pushes new led raw values (if enabled)
|
||||
/// @param ledColors The current ledColors
|
||||
///
|
||||
void streamLedcolorsUpdate(const std::vector<ColorRgb>& ledColors);
|
||||
|
||||
/// push images whenever hyperion emits (if enabled)
|
||||
void setImage(const Image<ColorRgb> & image);
|
||||
@ -80,11 +60,9 @@ signals:
|
||||
void forwardJsonMessage(QJsonObject);
|
||||
|
||||
private:
|
||||
|
||||
// The JsonCB instance which handles data subscription/notifications
|
||||
JsonCB* _jsonCB;
|
||||
// true if further callbacks are forbidden (http)
|
||||
bool _noListener;
|
||||
|
||||
/// The peer address of the client
|
||||
QString _peerAddress;
|
||||
|
||||
@ -94,8 +72,8 @@ private:
|
||||
/// Hyperion instance
|
||||
Hyperion* _hyperion;
|
||||
|
||||
/// timer for ledcolors streaming
|
||||
QTimer _timer_ledcolors;
|
||||
// The JsonCB instance which handles data subscription/notifications
|
||||
JsonCB* _jsonCB;
|
||||
|
||||
// streaming buffers
|
||||
QJsonObject _streaming_leds_reply;
|
||||
@ -108,9 +86,15 @@ private:
|
||||
/// mutex to determine state of image streaming
|
||||
QMutex _image_stream_mutex;
|
||||
|
||||
/// mutex to determine state of image streaming
|
||||
QMutex _led_stream_mutex;
|
||||
|
||||
/// timeout for live video refresh
|
||||
volatile qint64 _image_stream_timeout;
|
||||
|
||||
/// timeout for led color refresh
|
||||
volatile qint64 _led_stream_timeout;
|
||||
|
||||
///
|
||||
/// Handle an incoming JSON Color message
|
||||
///
|
||||
|
@ -17,30 +17,43 @@
|
||||
#include <effectengine/EffectSchema.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
// pre-declarioation
|
||||
// pre-declaration
|
||||
class Effect;
|
||||
class EffectFileHandler;
|
||||
|
||||
class EffectEngine : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EffectEngine(Hyperion * hyperion, const QJsonObject & jsonEffectConfig);
|
||||
EffectEngine(Hyperion * hyperion);
|
||||
virtual ~EffectEngine();
|
||||
|
||||
void readEffects();
|
||||
|
||||
const std::list<EffectDefinition> & getEffects() const
|
||||
{
|
||||
return _availableEffects;
|
||||
};
|
||||
const std::list<EffectDefinition> & getEffects() const { return _availableEffects; };
|
||||
|
||||
const std::list<ActiveEffectDefinition> & getActiveEffects();
|
||||
|
||||
const std::list<EffectSchema> & getEffectSchemas()
|
||||
{
|
||||
return _effectSchemas;
|
||||
};
|
||||
///
|
||||
/// Get available schemas from EffectFileHandler
|
||||
/// @return all schemas
|
||||
///
|
||||
const std::list<EffectSchema> & getEffectSchemas();
|
||||
|
||||
///
|
||||
/// @brief Save an effect with EffectFileHandler
|
||||
/// @param obj The effect args
|
||||
/// @param[out] resultMsg The feedback message
|
||||
/// @return True on success else false
|
||||
///
|
||||
const bool saveEffect(const QJsonObject& obj, QString& resultMsg);
|
||||
|
||||
///
|
||||
/// @brief Delete an effect by name.
|
||||
/// @param[in] effectName The effect name to delete
|
||||
/// @param[out] resultMsg The message on error
|
||||
/// @return True on success else false
|
||||
///
|
||||
const bool deleteEffect(const QString& effectName, QString& resultMsg);
|
||||
|
||||
///
|
||||
/// @brief Get all init data of the running effects and stop them
|
||||
@ -72,19 +85,18 @@ public slots:
|
||||
private slots:
|
||||
void effectFinished();
|
||||
|
||||
///
|
||||
/// @brief is called whenever the EffectFileHandler emits updated effect list
|
||||
///
|
||||
void handleUpdatedEffectList();
|
||||
|
||||
private:
|
||||
bool loadEffectDefinition(const QString & path, const QString & effectConfigFile, EffectDefinition &effectDefinition);
|
||||
|
||||
bool loadEffectSchema(const QString & path, const QString & effectSchemaFile, EffectSchema &effectSchema);
|
||||
|
||||
/// 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);
|
||||
|
||||
private:
|
||||
Hyperion * _hyperion;
|
||||
|
||||
QJsonObject _effectConfig;
|
||||
|
||||
std::list<EffectDefinition> _availableEffects;
|
||||
|
||||
std::list<Effect *> _activeEffects;
|
||||
@ -93,7 +105,8 @@ private:
|
||||
|
||||
std::list<ActiveEffectDefinition> _cachedActiveEffects;
|
||||
|
||||
std::list<EffectSchema> _effectSchemas;
|
||||
|
||||
Logger * _log;
|
||||
|
||||
// The global effect file handler
|
||||
EffectFileHandler* _effectFileHandler;
|
||||
};
|
||||
|
86
include/effectengine/EffectFileHandler.h
Normal file
86
include/effectengine/EffectFileHandler.h
Normal file
@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
// util
|
||||
#include <utils/Logger.h>
|
||||
#include <effectengine/EffectDefinition.h>
|
||||
#include <effectengine/EffectSchema.h>
|
||||
#include <utils/settings.h>
|
||||
|
||||
class EffectFileHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
friend class HyperionDaemon;
|
||||
EffectFileHandler(const QString& rootPath, const QJsonDocument& effectConfig, QObject* parent = nullptr);
|
||||
|
||||
public:
|
||||
static EffectFileHandler* efhInstance;
|
||||
static EffectFileHandler* getInstance() { return efhInstance; };
|
||||
|
||||
///
|
||||
/// @brief Get all available effects
|
||||
///
|
||||
const std::list<EffectDefinition> & getEffects() const { return _availableEffects; };
|
||||
|
||||
///
|
||||
/// @brief Get all available schemas
|
||||
///
|
||||
const std::list<EffectSchema> & getEffectSchemas() { return _effectSchemas; };
|
||||
|
||||
///
|
||||
/// @brief Save an effect
|
||||
/// @param obj The effect args
|
||||
/// @param[out] resultMsg The feedback message
|
||||
/// @return True on success else false
|
||||
///
|
||||
const bool saveEffect(const QJsonObject& obj, QString& resultMsg);
|
||||
|
||||
///
|
||||
/// @brief Delete an effect by name.
|
||||
/// @param[in] effectName The effect name to delete
|
||||
/// @param[out] resultMsg The message on error
|
||||
/// @return True on success else false
|
||||
///
|
||||
const bool deleteEffect(const QString& effectName, QString& resultMsg);
|
||||
|
||||
public slots:
|
||||
///
|
||||
/// @brief Handle settings update from Hyperion Settingsmanager emit
|
||||
/// @param type settingyType from enum
|
||||
/// @param config configuration object
|
||||
///
|
||||
void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config);
|
||||
|
||||
signals:
|
||||
///
|
||||
/// @brief Emits whenever the data changes for an effect
|
||||
///
|
||||
void effectListChanged();
|
||||
|
||||
private:
|
||||
///
|
||||
/// @brief refresh available schemas and effects
|
||||
///
|
||||
void updateEffects();
|
||||
|
||||
///
|
||||
/// @brief Load the effect definition, called by updateEffects()
|
||||
///
|
||||
bool loadEffectDefinition(const QString & path, const QString & effectConfigFile, EffectDefinition &effectDefinition);
|
||||
|
||||
///
|
||||
/// @brief load effect schemas, called by updateEffects()
|
||||
///
|
||||
bool loadEffectSchema(const QString & path, const QString & effectSchemaFile, EffectSchema &effectSchema);
|
||||
|
||||
private:
|
||||
QJsonObject _effectConfig;
|
||||
Logger* _log;
|
||||
const QString _rootPath;
|
||||
|
||||
// available effects
|
||||
std::list<EffectDefinition> _availableEffects;
|
||||
|
||||
// all schemas
|
||||
std::list<EffectSchema> _effectSchemas;
|
||||
};
|
@ -54,6 +54,11 @@ private slots:
|
||||
///
|
||||
void setV4lInactive();
|
||||
|
||||
///
|
||||
/// @brief Is called from _systemInactiveTimer to set source after specific time to inactive
|
||||
///
|
||||
void setSystemInactive();
|
||||
|
||||
private:
|
||||
/// Hyperion instance
|
||||
Hyperion* _hyperion;
|
||||
@ -61,6 +66,7 @@ private:
|
||||
/// Reflect state of System capture and prio
|
||||
bool _systemCaptEnabled;
|
||||
quint8 _systemCaptPrio;
|
||||
QTimer* _systemInactiveTimer;
|
||||
|
||||
/// Reflect state of v4l capture and prio
|
||||
bool _v4lCaptEnabled;
|
||||
|
@ -163,8 +163,21 @@ public:
|
||||
///
|
||||
const InputInfo getPriorityInfo(const int priority) const;
|
||||
|
||||
/// Reload the list of available effects
|
||||
void reloadEffects();
|
||||
///
|
||||
/// @brief Save an effect
|
||||
/// @param obj The effect args
|
||||
/// @param[out] resultMsg The feedback message
|
||||
/// @return True on success else false
|
||||
///
|
||||
const bool saveEffect(const QJsonObject& obj, QString& resultMsg);
|
||||
|
||||
///
|
||||
/// @brief Delete an effect by name.
|
||||
/// @param[in] effectName The effect name to delete
|
||||
/// @param[out] resultMsg The message on error
|
||||
/// @return True on success else false
|
||||
///
|
||||
const bool deleteEffect(const QString& effectName, QString& resultMsg);
|
||||
|
||||
/// Get the list of available effects
|
||||
/// @return The list of available effects
|
||||
@ -215,12 +228,6 @@ public:
|
||||
/// @return the state
|
||||
bool sourceAutoSelectEnabled();
|
||||
|
||||
///
|
||||
/// @brief Get the last untransformed/unadjusted led colors
|
||||
/// @return The _rawLedBuffer leds
|
||||
///
|
||||
const std::vector<ColorRgb>& getRawLedBuffer() { return _rawLedBuffer; };
|
||||
|
||||
///
|
||||
/// @brief Enable/Disable components during runtime, called from external API (requests)
|
||||
///
|
||||
@ -433,6 +440,11 @@ signals:
|
||||
///
|
||||
void v4lImage(const Image<ColorRgb> & image);
|
||||
|
||||
///
|
||||
/// @brief Emits whenever new untransformed ledColos data is available, reflects the current visible device
|
||||
///
|
||||
void rawLedColors(const std::vector<ColorRgb>& ledValues);
|
||||
|
||||
private slots:
|
||||
///
|
||||
/// Updates the priority muxer with the current time and (re)writes the led color with applied
|
||||
@ -548,8 +560,6 @@ private:
|
||||
|
||||
/// buffer for leds (with adjustment)
|
||||
std::vector<ColorRgb> _ledBuffer;
|
||||
/// buffer for leds (without adjustment)
|
||||
std::vector<ColorRgb> _rawLedBuffer;
|
||||
|
||||
/// Boblight instance
|
||||
BoblightServer* _boblightServer;
|
||||
|
@ -9,7 +9,7 @@
|
||||
class Hyperion;
|
||||
|
||||
///
|
||||
/// @brief Manage the settings read write from/to database, on settings changed will emit a signal to update components accordingly
|
||||
/// @brief Manage the settings read write from/to config file, on settings changed will emit a signal to update components accordingly
|
||||
///
|
||||
class SettingsManager : public QObject
|
||||
{
|
||||
@ -37,7 +37,7 @@ public:
|
||||
const bool saveSettings(QJsonObject config, const bool& correct = false);
|
||||
|
||||
///
|
||||
/// @brief get a single setting json from database
|
||||
/// @brief get a single setting json from config
|
||||
/// @param type The settings::type from enum
|
||||
/// @return The requested json data as QJsonDocument
|
||||
///
|
||||
@ -51,7 +51,7 @@ public:
|
||||
|
||||
signals:
|
||||
///
|
||||
/// @brief Emits whenever a config part changed. Comparison of database and new data to prevent false positive
|
||||
/// @brief Emits whenever a config part changed.
|
||||
/// @param type The settings type from enum
|
||||
/// @param data The data as QJsonDocument
|
||||
///
|
||||
@ -60,10 +60,13 @@ signals:
|
||||
private:
|
||||
/// Hyperion instance
|
||||
Hyperion* _hyperion;
|
||||
|
||||
/// Logger instance
|
||||
Logger* _log;
|
||||
|
||||
/// the schema
|
||||
static QJsonObject schemaJson;
|
||||
|
||||
/// the current config of this instance
|
||||
QJsonObject _qconfig;
|
||||
};
|
||||
|
@ -12,9 +12,9 @@
|
||||
#include <QImage>
|
||||
#include <QBuffer>
|
||||
#include <QByteArray>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QIODevice>
|
||||
// #include <QFileInfo>
|
||||
// #include <QDir>
|
||||
// #include <QIODevice>
|
||||
#include <QDateTime>
|
||||
|
||||
// hyperion includes
|
||||
@ -41,13 +41,14 @@ using namespace hyperion;
|
||||
|
||||
JsonAPI::JsonAPI(QString peerAddress, Logger* log, QObject* parent, bool noListener)
|
||||
: QObject(parent)
|
||||
, _jsonCB(new JsonCB(this))
|
||||
, _noListener(noListener)
|
||||
, _peerAddress(peerAddress)
|
||||
, _log(log)
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _jsonCB(new JsonCB(this))
|
||||
, _streaming_logging_activated(false)
|
||||
, _image_stream_timeout(0)
|
||||
, _led_stream_timeout(0)
|
||||
{
|
||||
// the JsonCB creates json messages you can subscribe to e.g. data change events; forward them to the parent client
|
||||
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
|
||||
@ -55,9 +56,8 @@ JsonAPI::JsonAPI(QString peerAddress, Logger* log, QObject* parent, bool noListe
|
||||
// notify hyperion about a jsonMessageForward
|
||||
connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage);
|
||||
|
||||
// led color stream update timer
|
||||
connect(&_timer_ledcolors, SIGNAL(timeout()), this, SLOT(streamLedcolorsUpdate()));
|
||||
_image_stream_mutex.unlock();
|
||||
_led_stream_mutex.unlock();
|
||||
}
|
||||
|
||||
void JsonAPI::handleMessage(const QString& messageString)
|
||||
@ -207,105 +207,20 @@ void JsonAPI::handleEffectCommand(const QJsonObject& message, const QString& com
|
||||
|
||||
void JsonAPI::handleCreateEffectCommand(const QJsonObject& message, const QString &command, const int tan)
|
||||
{
|
||||
if (!message["args"].toObject().isEmpty())
|
||||
{
|
||||
QString scriptName;
|
||||
(message["script"].toString().mid(0, 1) == ":" )
|
||||
? scriptName = ":/effects//" + message["script"].toString().mid(1)
|
||||
: scriptName = message["script"].toString();
|
||||
|
||||
std::list<EffectSchema> effectsSchemas = _hyperion->getEffectSchemas();
|
||||
std::list<EffectSchema>::iterator it = std::find_if(effectsSchemas.begin(), effectsSchemas.end(), find_schema(scriptName));
|
||||
|
||||
if (it != effectsSchemas.end())
|
||||
{
|
||||
if(!JsonUtils::validate("JsonRpc@"+_peerAddress, message["args"].toObject(), it->schemaFile, _log))
|
||||
{
|
||||
sendErrorReply("Error during arg validation against schema, please consult the Hyperion Log", command, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject effectJson;
|
||||
QJsonArray effectArray;
|
||||
effectArray = _hyperion->getQJsonConfig()["effects"].toObject()["paths"].toArray();
|
||||
|
||||
if (effectArray.size() > 0)
|
||||
{
|
||||
if (message["name"].toString().trimmed().isEmpty() || message["name"].toString().trimmed().startsWith("."))
|
||||
{
|
||||
sendErrorReply("Can't save new effect. Effect name is empty or begins with a dot.", command, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
effectJson["name"] = message["name"].toString();
|
||||
effectJson["script"] = message["script"].toString();
|
||||
effectJson["args"] = message["args"].toObject();
|
||||
|
||||
std::list<EffectDefinition> availableEffects = _hyperion->getEffects();
|
||||
std::list<EffectDefinition>::iterator iter = std::find_if(availableEffects.begin(), availableEffects.end(), find_effect(message["name"].toString()));
|
||||
|
||||
QFileInfo newFileName;
|
||||
if (iter != availableEffects.end())
|
||||
{
|
||||
newFileName.setFile(iter->file);
|
||||
if (newFileName.absoluteFilePath().mid(0, 1) == ":")
|
||||
{
|
||||
sendErrorReply("The effect name '" + message["name"].toString() + "' is assigned to an internal effect. Please rename your effekt.", command, tan);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
{
|
||||
QString f = FileUtils::convertPath(effectArray[0].toString() + "/" + message["name"].toString().replace(QString(" "), QString("")) + QString(".json"));
|
||||
newFileName.setFile(f);
|
||||
}
|
||||
|
||||
if(!JsonUtils::write(newFileName.absoluteFilePath(), effectJson, _log))
|
||||
{
|
||||
sendErrorReply("Error while saving effect, please check the Hyperion Log", command, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
Info(_log, "Reload effect list");
|
||||
_hyperion->reloadEffects();
|
||||
sendSuccessReply(command, tan);
|
||||
} else
|
||||
{
|
||||
sendErrorReply("Can't save new effect. Effect path empty", command, tan);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
sendErrorReply("Missing schema file for Python script " + message["script"].toString(), command, tan);
|
||||
} else
|
||||
sendErrorReply("Missing or empty Object 'args'", command, tan);
|
||||
QString resultMsg;
|
||||
if(_hyperion->saveEffect(message, resultMsg))
|
||||
sendSuccessReply(command, tan);
|
||||
else
|
||||
sendErrorReply(resultMsg, command, tan);
|
||||
}
|
||||
|
||||
void JsonAPI::handleDeleteEffectCommand(const QJsonObject& message, const QString& command, const int tan)
|
||||
{
|
||||
QString effectName = message["name"].toString();
|
||||
std::list<EffectDefinition> effectsDefinition = _hyperion->getEffects();
|
||||
std::list<EffectDefinition>::iterator it = std::find_if(effectsDefinition.begin(), effectsDefinition.end(), find_effect(effectName));
|
||||
|
||||
if (it != effectsDefinition.end())
|
||||
{
|
||||
QFileInfo effectConfigurationFile(it->file);
|
||||
if (effectConfigurationFile.absoluteFilePath().mid(0, 1) != ":" )
|
||||
{
|
||||
if (effectConfigurationFile.exists())
|
||||
{
|
||||
bool result = QFile::remove(effectConfigurationFile.absoluteFilePath());
|
||||
if (result)
|
||||
{
|
||||
Info(_log, "Reload effect list");
|
||||
_hyperion->reloadEffects();
|
||||
sendSuccessReply(command, tan);
|
||||
} else
|
||||
sendErrorReply("Can't delete effect configuration file: " + effectConfigurationFile.absoluteFilePath() + ". Please check permissions", command, tan);
|
||||
} else
|
||||
sendErrorReply("Can't find effect configuration file: " + effectConfigurationFile.absoluteFilePath(), command, tan);
|
||||
} else
|
||||
sendErrorReply("Can't delete internal effect: " + message["name"].toString(), command, tan);
|
||||
} else
|
||||
sendErrorReply("Effect " + message["name"].toString() + " not found", command, tan);
|
||||
QString resultMsg;
|
||||
if(_hyperion->deleteEffect(message["name"].toString(), resultMsg))
|
||||
sendSuccessReply(command, tan);
|
||||
else
|
||||
sendErrorReply(resultMsg, command, tan);
|
||||
}
|
||||
|
||||
void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, const int tan)
|
||||
@ -358,10 +273,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
|
||||
const Hyperion::InputInfo & priorityInfo = _hyperion->getPriorityInfo(priority);
|
||||
QJsonObject item;
|
||||
item["priority"] = priority;
|
||||
if (int(priorityInfo.timeoutTime_ms - now) > -1 )
|
||||
{
|
||||
if (priorityInfo.timeoutTime_ms > 0 )
|
||||
item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now);
|
||||
}
|
||||
|
||||
// owner has optional informations to the component
|
||||
if(!priorityInfo.owner.isEmpty())
|
||||
item["owner"] = priorityInfo.owner;
|
||||
@ -861,12 +775,11 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString &
|
||||
_streaming_leds_reply["success"] = true;
|
||||
_streaming_leds_reply["command"] = command+"-ledstream-update";
|
||||
_streaming_leds_reply["tan"] = tan;
|
||||
_timer_ledcolors.setInterval(125);
|
||||
_timer_ledcolors.start(125);
|
||||
connect(_hyperion, &Hyperion::rawLedColors, this, &JsonAPI::streamLedcolorsUpdate, Qt::UniqueConnection);
|
||||
}
|
||||
else if (subcommand == "ledstream-stop")
|
||||
{
|
||||
_timer_ledcolors.stop();
|
||||
disconnect(_hyperion, &Hyperion::rawLedColors, this, &JsonAPI::streamLedcolorsUpdate);
|
||||
}
|
||||
else if (subcommand == "imagestream-start")
|
||||
{
|
||||
@ -984,32 +897,37 @@ void JsonAPI::sendErrorReply(const QString &error, const QString &command, const
|
||||
}
|
||||
|
||||
|
||||
void JsonAPI::streamLedcolorsUpdate()
|
||||
void JsonAPI::streamLedcolorsUpdate(const std::vector<ColorRgb>& ledColors)
|
||||
{
|
||||
QJsonObject result;
|
||||
QJsonArray leds;
|
||||
|
||||
const std::vector<ColorRgb> & ledColors = _hyperion->getRawLedBuffer();
|
||||
for(auto color = ledColors.begin(); color != ledColors.end(); ++color)
|
||||
if ( (_led_stream_timeout+100) < QDateTime::currentMSecsSinceEpoch() && _led_stream_mutex.tryLock(0) )
|
||||
{
|
||||
QJsonObject item;
|
||||
item["index"] = int(color - ledColors.begin());
|
||||
item["red"] = color->red;
|
||||
item["green"] = color->green;
|
||||
item["blue"] = color->blue;
|
||||
leds.append(item);
|
||||
_led_stream_timeout = QDateTime::currentMSecsSinceEpoch();
|
||||
QJsonObject result;
|
||||
QJsonArray leds;
|
||||
|
||||
for(auto color = ledColors.begin(); color != ledColors.end(); ++color)
|
||||
{
|
||||
QJsonObject item;
|
||||
item["index"] = int(color - ledColors.begin());
|
||||
item["red"] = color->red;
|
||||
item["green"] = color->green;
|
||||
item["blue"] = color->blue;
|
||||
leds.append(item);
|
||||
}
|
||||
|
||||
result["leds"] = leds;
|
||||
_streaming_leds_reply["result"] = result;
|
||||
|
||||
// send the result
|
||||
emit callbackMessage(_streaming_leds_reply);
|
||||
|
||||
_led_stream_mutex.unlock();
|
||||
}
|
||||
|
||||
result["leds"] = leds;
|
||||
_streaming_leds_reply["result"] = result;
|
||||
|
||||
// send the result
|
||||
emit callbackMessage(_streaming_leds_reply);
|
||||
}
|
||||
|
||||
void JsonAPI::setImage(const Image<ColorRgb> & image)
|
||||
{
|
||||
if ( (_image_stream_timeout+250) < QDateTime::currentMSecsSinceEpoch() && _image_stream_mutex.tryLock(0) )
|
||||
if ( (_image_stream_timeout+100) < QDateTime::currentMSecsSinceEpoch() && _image_stream_mutex.tryLock(0) )
|
||||
{
|
||||
_image_stream_timeout = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
|
@ -139,10 +139,9 @@ void JsonCB::handlePriorityUpdate()
|
||||
const Hyperion::InputInfo priorityInfo = _prioMuxer->getInputInfo(priority);
|
||||
QJsonObject item;
|
||||
item["priority"] = priority;
|
||||
if (int(priorityInfo.timeoutTime_ms - now) > -1 )
|
||||
{
|
||||
if (priorityInfo.timeoutTime_ms > 0 )
|
||||
item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now);
|
||||
}
|
||||
|
||||
// owner has optional informations to the component
|
||||
if(!priorityInfo.owner.isEmpty())
|
||||
item["owner"] = priorityInfo.owner;
|
||||
|
@ -18,33 +18,46 @@
|
||||
#include <effectengine/EffectEngine.h>
|
||||
#include <effectengine/Effect.h>
|
||||
#include <effectengine/EffectModule.h>
|
||||
#include <effectengine/EffectFileHandler.h>
|
||||
#include "HyperionConfig.h"
|
||||
|
||||
EffectEngine::EffectEngine(Hyperion * hyperion, const QJsonObject & jsonEffectConfig)
|
||||
EffectEngine::EffectEngine(Hyperion * hyperion)
|
||||
: _hyperion(hyperion)
|
||||
, _effectConfig(jsonEffectConfig)
|
||||
, _availableEffects()
|
||||
, _activeEffects()
|
||||
, _log(Logger::getInstance("EFFECTENGINE"))
|
||||
, _effectFileHandler(EffectFileHandler::getInstance())
|
||||
{
|
||||
|
||||
Q_INIT_RESOURCE(EffectEngine);
|
||||
qRegisterMetaType<std::vector<ColorRgb>>("std::vector<ColorRgb>");
|
||||
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||
qRegisterMetaType<hyperion::Components>("hyperion::Components");
|
||||
|
||||
// connect the Hyperion channel clear feedback
|
||||
connect(_hyperion, SIGNAL(channelCleared(int)), this, SLOT(channelCleared(int)));
|
||||
connect(_hyperion, SIGNAL(allChannelsCleared()), this, SLOT(allChannelsCleared()));
|
||||
|
||||
// read all effects
|
||||
readEffects();
|
||||
// get notifications about refreshed effect list
|
||||
connect(_effectFileHandler, &EffectFileHandler::effectListChanged, this, &EffectEngine::handleUpdatedEffectList);
|
||||
|
||||
// register smooth cfgs and fill available effects
|
||||
handleUpdatedEffectList();
|
||||
}
|
||||
|
||||
EffectEngine::~EffectEngine()
|
||||
{
|
||||
}
|
||||
|
||||
const bool EffectEngine::saveEffect(const QJsonObject& obj, QString& resultMsg)
|
||||
{
|
||||
return _effectFileHandler->saveEffect(obj, resultMsg);
|
||||
}
|
||||
|
||||
const bool EffectEngine::deleteEffect(const QString& effectName, QString& resultMsg)
|
||||
{
|
||||
return _effectFileHandler->deleteEffect(effectName, resultMsg);
|
||||
}
|
||||
|
||||
const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()
|
||||
{
|
||||
_availableActiveEffects.clear();
|
||||
@ -63,6 +76,11 @@ const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()
|
||||
return _availableActiveEffects;
|
||||
}
|
||||
|
||||
const std::list<EffectSchema> & EffectEngine::getEffectSchemas()
|
||||
{
|
||||
return _effectFileHandler->getEffectSchemas();
|
||||
}
|
||||
|
||||
void EffectEngine::cacheRunningEffects()
|
||||
{
|
||||
_cachedActiveEffects.clear();
|
||||
@ -90,175 +108,26 @@ void EffectEngine::startCachedEffects()
|
||||
_cachedActiveEffects.clear();
|
||||
}
|
||||
|
||||
bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effectConfigFile, EffectDefinition & effectDefinition)
|
||||
void EffectEngine::handleUpdatedEffectList()
|
||||
{
|
||||
QString fileName = path + QDir::separator() + effectConfigFile;
|
||||
|
||||
// Read and parse the effect json config file
|
||||
QJsonObject configEffect;
|
||||
if(!JsonUtils::readFile(fileName, configEffect, _log))
|
||||
return false;
|
||||
|
||||
Q_INIT_RESOURCE(EffectEngine);
|
||||
// validate effect config with effect schema(path)
|
||||
if(!JsonUtils::validate(fileName, configEffect, ":effect-schema", _log))
|
||||
return false;
|
||||
|
||||
// setup the definition
|
||||
effectDefinition.file = fileName;
|
||||
QJsonObject config = configEffect;
|
||||
QString scriptName = config["script"].toString();
|
||||
effectDefinition.name = config["name"].toString();
|
||||
if (scriptName.isEmpty())
|
||||
return false;
|
||||
|
||||
QFile fileInfo(scriptName);
|
||||
|
||||
if (scriptName.mid(0, 1) == ":" )
|
||||
{
|
||||
(!fileInfo.exists())
|
||||
? effectDefinition.script = ":/effects/"+scriptName.mid(1)
|
||||
: effectDefinition.script = scriptName;
|
||||
} else
|
||||
{
|
||||
(!fileInfo.exists())
|
||||
? effectDefinition.script = path + QDir::separator() + scriptName
|
||||
: effectDefinition.script = scriptName;
|
||||
}
|
||||
|
||||
effectDefinition.args = config["args"].toObject();
|
||||
effectDefinition.smoothCfg = SMOOTHING_MODE_PAUSE;
|
||||
if (effectDefinition.args["smoothing-custom-settings"].toBool())
|
||||
{
|
||||
effectDefinition.smoothCfg = _hyperion->addSmoothingConfig(
|
||||
effectDefinition.args["smoothing-time_ms"].toInt(),
|
||||
effectDefinition.args["smoothing-updateFrequency"].toDouble(),
|
||||
0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
effectDefinition.smoothCfg = _hyperion->addSmoothingConfig(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EffectEngine::loadEffectSchema(const QString &path, const QString &effectSchemaFile, EffectSchema & effectSchema)
|
||||
{
|
||||
QString fileName = path + "schema/" + QDir::separator() + effectSchemaFile;
|
||||
|
||||
// Read and parse the effect schema file
|
||||
QJsonObject schemaEffect;
|
||||
if(!JsonUtils::readFile(fileName, schemaEffect, _log))
|
||||
return false;
|
||||
|
||||
// setup the definition
|
||||
QString scriptName = schemaEffect["script"].toString();
|
||||
effectSchema.schemaFile = fileName;
|
||||
fileName = path + QDir::separator() + scriptName;
|
||||
QFile pyFile(fileName);
|
||||
|
||||
if (scriptName.isEmpty() || !pyFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
fileName = path + "schema/" + QDir::separator() + effectSchemaFile;
|
||||
Error( _log, "Python script '%s' in effect schema '%s' could not be loaded", QSTRING_CSTR(scriptName), QSTRING_CSTR(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
pyFile.close();
|
||||
|
||||
effectSchema.pyFile = (scriptName.mid(0, 1) == ":" ) ? ":/effects/"+scriptName.mid(1) : path + QDir::separator() + scriptName;
|
||||
effectSchema.pySchema = schemaEffect;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EffectEngine::readEffects()
|
||||
{
|
||||
// clear all lists
|
||||
_availableEffects.clear();
|
||||
_effectSchemas.clear();
|
||||
|
||||
// read all effects
|
||||
const QJsonArray & paths = _effectConfig["paths"].toArray();
|
||||
const QJsonArray & disabledEfx = _effectConfig["disable"].toArray();
|
||||
|
||||
QStringList efxPathList;
|
||||
efxPathList << ":/effects/";
|
||||
QStringList disableList;
|
||||
|
||||
for(auto p : paths)
|
||||
for (auto def : _effectFileHandler->getEffects())
|
||||
{
|
||||
efxPathList << p.toString().replace("$ROOT",_hyperion->getRootPath());
|
||||
}
|
||||
for(auto efx : disabledEfx)
|
||||
{
|
||||
disableList << efx.toString();
|
||||
}
|
||||
|
||||
QMap<QString, EffectDefinition> availableEffects;
|
||||
for (const QString & path : efxPathList )
|
||||
{
|
||||
QDir directory(path);
|
||||
if (!directory.exists())
|
||||
// add smoothing configs to Hyperion
|
||||
if (def.args["smoothing-custom-settings"].toBool())
|
||||
{
|
||||
if(directory.mkpath(path))
|
||||
{
|
||||
Warning(_log, "New Effect path \"%s\" created successfull", QSTRING_CSTR(path) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(_log, "Failed to create Effect path \"%s\", please check permissions", QSTRING_CSTR(path) );
|
||||
}
|
||||
def.smoothCfg = _hyperion->addSmoothingConfig(
|
||||
def.args["smoothing-time_ms"].toInt(),
|
||||
def.args["smoothing-updateFrequency"].toDouble(),
|
||||
0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
int efxCount = 0;
|
||||
QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString & filename : filenames)
|
||||
{
|
||||
EffectDefinition def;
|
||||
if (loadEffectDefinition(path, filename, def))
|
||||
{
|
||||
InfoIf(availableEffects.find(def.name) != availableEffects.end(), _log,
|
||||
"effect overload effect '%s' is now taken from '%s'", QSTRING_CSTR(def.name), QSTRING_CSTR(path) );
|
||||
|
||||
if ( disableList.contains(def.name) )
|
||||
{
|
||||
Info(_log, "effect '%s' not loaded, because it is disabled in hyperion config", QSTRING_CSTR(def.name));
|
||||
}
|
||||
else
|
||||
{
|
||||
availableEffects[def.name] = def;
|
||||
efxCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Info(_log, "%d effects loaded from directory %s", efxCount, QSTRING_CSTR(path));
|
||||
|
||||
// collect effect schemas
|
||||
efxCount = 0;
|
||||
directory = path.endsWith("/") ? (path + "schema/") : (path + "/schema/");
|
||||
QStringList pynames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString & pyname : pynames)
|
||||
{
|
||||
EffectSchema pyEffect;
|
||||
if (loadEffectSchema(path, pyname, pyEffect))
|
||||
{
|
||||
_effectSchemas.push_back(pyEffect);
|
||||
efxCount++;
|
||||
}
|
||||
}
|
||||
InfoIf(efxCount > 0, _log, "%d effect schemas loaded from directory %s", efxCount, QSTRING_CSTR((path + "schema/")));
|
||||
def.smoothCfg = _hyperion->addSmoothingConfig(true);
|
||||
}
|
||||
_availableEffects.push_back(def);
|
||||
}
|
||||
|
||||
for(auto item : availableEffects)
|
||||
{
|
||||
_availableEffects.push_back(item);
|
||||
}
|
||||
|
||||
ErrorIf(_availableEffects.size()==0, _log, "no effects found, check your effect directories");
|
||||
|
||||
emit effectListUpdated();
|
||||
}
|
||||
|
||||
|
322
libsrc/effectengine/EffectFileHandler.cpp
Normal file
322
libsrc/effectengine/EffectFileHandler.cpp
Normal file
@ -0,0 +1,322 @@
|
||||
#include <effectengine/EffectFileHandler.h>
|
||||
|
||||
// util
|
||||
#include <utils/JsonUtils.h>
|
||||
|
||||
// qt
|
||||
#include <QJsonArray>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QMap>
|
||||
|
||||
// createEffect helper
|
||||
struct find_schema: std::unary_function<EffectSchema, bool>
|
||||
{
|
||||
QString pyFile;
|
||||
find_schema(QString pyFile):pyFile(pyFile) { }
|
||||
bool operator()(EffectSchema const& schema) const
|
||||
{
|
||||
return schema.pyFile == pyFile;
|
||||
}
|
||||
};
|
||||
|
||||
// deleteEffect helper
|
||||
struct find_effect: std::unary_function<EffectDefinition, bool>
|
||||
{
|
||||
QString effectName;
|
||||
find_effect(QString effectName) :effectName(effectName) { }
|
||||
bool operator()(EffectDefinition const& effectDefinition) const
|
||||
{
|
||||
return effectDefinition.name == effectName;
|
||||
}
|
||||
};
|
||||
|
||||
EffectFileHandler* EffectFileHandler::efhInstance;
|
||||
|
||||
EffectFileHandler::EffectFileHandler(const QString& rootPath, const QJsonDocument& effectConfig, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _effectConfig()
|
||||
, _log(Logger::getInstance("EFFECTFILES"))
|
||||
, _rootPath(rootPath)
|
||||
{
|
||||
EffectFileHandler::efhInstance = this;
|
||||
|
||||
Q_INIT_RESOURCE(EffectEngine);
|
||||
|
||||
// init
|
||||
handleSettingsUpdate(settings::EFFECTS, effectConfig);
|
||||
}
|
||||
|
||||
void EffectFileHandler::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::EFFECTS)
|
||||
{
|
||||
_effectConfig = config.object();
|
||||
// update effects and schemas
|
||||
updateEffects();
|
||||
}
|
||||
}
|
||||
|
||||
const bool EffectFileHandler::deleteEffect(const QString& effectName, QString& resultMsg)
|
||||
{
|
||||
std::list<EffectDefinition> effectsDefinition = getEffects();
|
||||
std::list<EffectDefinition>::iterator it = std::find_if(effectsDefinition.begin(), effectsDefinition.end(), find_effect(effectName));
|
||||
|
||||
if (it != effectsDefinition.end())
|
||||
{
|
||||
QFileInfo effectConfigurationFile(it->file);
|
||||
if (effectConfigurationFile.absoluteFilePath().mid(0, 1) != ":" )
|
||||
{
|
||||
if (effectConfigurationFile.exists())
|
||||
{
|
||||
bool result = QFile::remove(effectConfigurationFile.absoluteFilePath());
|
||||
if (result)
|
||||
{
|
||||
updateEffects();
|
||||
return true;
|
||||
} else
|
||||
resultMsg = "Can't delete effect configuration file: " + effectConfigurationFile.absoluteFilePath() + ". Please check permissions";
|
||||
} else
|
||||
resultMsg = "Can't find effect configuration file: " + effectConfigurationFile.absoluteFilePath();
|
||||
} else
|
||||
resultMsg = "Can't delete internal effect: " + effectName;
|
||||
} else
|
||||
resultMsg = "Effect " + effectName + " not found";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool EffectFileHandler::saveEffect(const QJsonObject& message, QString& resultMsg)
|
||||
{
|
||||
if (!message["args"].toObject().isEmpty())
|
||||
{
|
||||
QString scriptName;
|
||||
(message["script"].toString().mid(0, 1) == ":" )
|
||||
? scriptName = ":/effects//" + message["script"].toString().mid(1)
|
||||
: scriptName = message["script"].toString();
|
||||
|
||||
std::list<EffectSchema> effectsSchemas = getEffectSchemas();
|
||||
std::list<EffectSchema>::iterator it = std::find_if(effectsSchemas.begin(), effectsSchemas.end(), find_schema(scriptName));
|
||||
|
||||
if (it != effectsSchemas.end())
|
||||
{
|
||||
if(!JsonUtils::validate("EffectFileHandler", message["args"].toObject(), it->schemaFile, _log))
|
||||
{
|
||||
resultMsg = "Error during arg validation against schema, please consult the Hyperion Log";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonObject effectJson;
|
||||
QJsonArray effectArray;
|
||||
effectArray = _effectConfig["paths"].toArray();
|
||||
|
||||
if (effectArray.size() > 0)
|
||||
{
|
||||
if (message["name"].toString().trimmed().isEmpty() || message["name"].toString().trimmed().startsWith("."))
|
||||
{
|
||||
resultMsg = "Can't save new effect. Effect name is empty or begins with a dot.";
|
||||
return false;
|
||||
}
|
||||
|
||||
effectJson["name"] = message["name"].toString();
|
||||
effectJson["script"] = message["script"].toString();
|
||||
effectJson["args"] = message["args"].toObject();
|
||||
|
||||
std::list<EffectDefinition> availableEffects = getEffects();
|
||||
std::list<EffectDefinition>::iterator iter = std::find_if(availableEffects.begin(), availableEffects.end(), find_effect(message["name"].toString()));
|
||||
|
||||
QFileInfo newFileName;
|
||||
if (iter != availableEffects.end())
|
||||
{
|
||||
newFileName.setFile(iter->file);
|
||||
if (newFileName.absoluteFilePath().mid(0, 1) == ":")
|
||||
{
|
||||
resultMsg = "The effect name '" + message["name"].toString() + "' is assigned to an internal effect. Please rename your effekt.";
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
{
|
||||
// TODO global special keyword handling
|
||||
QString f = effectArray[0].toString().replace("$ROOT",_rootPath) + "/" + message["name"].toString().replace(QString(" "), QString("")) + QString(".json");
|
||||
newFileName.setFile(f);
|
||||
}
|
||||
|
||||
if(!JsonUtils::write(newFileName.absoluteFilePath(), effectJson, _log))
|
||||
{
|
||||
resultMsg = "Error while saving effect, please check the Hyperion Log";
|
||||
return false;
|
||||
}
|
||||
|
||||
Info(_log, "Reload effect list");
|
||||
updateEffects();
|
||||
resultMsg = "";
|
||||
return true;
|
||||
} else
|
||||
resultMsg = "Can't save new effect. Effect path empty";
|
||||
} else
|
||||
resultMsg = "Missing schema file for Python script " + message["script"].toString();
|
||||
} else
|
||||
resultMsg = "Missing or empty Object 'args'";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EffectFileHandler::updateEffects()
|
||||
{
|
||||
// clear all lists
|
||||
_availableEffects.clear();
|
||||
_effectSchemas.clear();
|
||||
|
||||
// read all effects
|
||||
const QJsonArray & paths = _effectConfig["paths"].toArray();
|
||||
const QJsonArray & disabledEfx = _effectConfig["disable"].toArray();
|
||||
|
||||
QStringList efxPathList;
|
||||
efxPathList << ":/effects/";
|
||||
QStringList disableList;
|
||||
|
||||
for(auto p : paths)
|
||||
{
|
||||
efxPathList << p.toString().replace("$ROOT",_rootPath);
|
||||
}
|
||||
for(auto efx : disabledEfx)
|
||||
{
|
||||
disableList << efx.toString();
|
||||
}
|
||||
|
||||
QMap<QString, EffectDefinition> availableEffects;
|
||||
for (const QString & path : efxPathList )
|
||||
{
|
||||
QDir directory(path);
|
||||
if (!directory.exists())
|
||||
{
|
||||
if(directory.mkpath(path))
|
||||
{
|
||||
Info(_log, "New Effect path \"%s\" created successfull", QSTRING_CSTR(path) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(_log, "Failed to create Effect path \"%s\", please check permissions", QSTRING_CSTR(path) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int efxCount = 0;
|
||||
QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString & filename : filenames)
|
||||
{
|
||||
EffectDefinition def;
|
||||
if (loadEffectDefinition(path, filename, def))
|
||||
{
|
||||
InfoIf(availableEffects.find(def.name) != availableEffects.end(), _log,
|
||||
"effect overload effect '%s' is now taken from '%s'", QSTRING_CSTR(def.name), QSTRING_CSTR(path) );
|
||||
|
||||
if ( disableList.contains(def.name) )
|
||||
{
|
||||
Info(_log, "effect '%s' not loaded, because it is disabled in hyperion config", QSTRING_CSTR(def.name));
|
||||
}
|
||||
else
|
||||
{
|
||||
availableEffects[def.name] = def;
|
||||
efxCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Info(_log, "%d effects loaded from directory %s", efxCount, QSTRING_CSTR(path));
|
||||
|
||||
// collect effect schemas
|
||||
efxCount = 0;
|
||||
directory = path.endsWith("/") ? (path + "schema/") : (path + "/schema/");
|
||||
QStringList pynames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString & pyname : pynames)
|
||||
{
|
||||
EffectSchema pyEffect;
|
||||
if (loadEffectSchema(path, pyname, pyEffect))
|
||||
{
|
||||
_effectSchemas.push_back(pyEffect);
|
||||
efxCount++;
|
||||
}
|
||||
}
|
||||
InfoIf(efxCount > 0, _log, "%d effect schemas loaded from directory %s", efxCount, QSTRING_CSTR((path + "schema/")));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto item : availableEffects)
|
||||
{
|
||||
_availableEffects.push_back(item);
|
||||
}
|
||||
|
||||
ErrorIf(_availableEffects.size()==0, _log, "no effects found, check your effect directories");
|
||||
|
||||
emit effectListChanged();
|
||||
}
|
||||
|
||||
bool EffectFileHandler::loadEffectDefinition(const QString &path, const QString &effectConfigFile, EffectDefinition & effectDefinition)
|
||||
{
|
||||
QString fileName = path + QDir::separator() + effectConfigFile;
|
||||
|
||||
// Read and parse the effect json config file
|
||||
QJsonObject configEffect;
|
||||
if(!JsonUtils::readFile(fileName, configEffect, _log))
|
||||
return false;
|
||||
|
||||
// validate effect config with effect schema(path)
|
||||
if(!JsonUtils::validate(fileName, configEffect, ":effect-schema", _log))
|
||||
return false;
|
||||
|
||||
// setup the definition
|
||||
effectDefinition.file = fileName;
|
||||
QJsonObject config = configEffect;
|
||||
QString scriptName = config["script"].toString();
|
||||
effectDefinition.name = config["name"].toString();
|
||||
if (scriptName.isEmpty())
|
||||
return false;
|
||||
|
||||
QFile fileInfo(scriptName);
|
||||
|
||||
if (scriptName.mid(0, 1) == ":" )
|
||||
{
|
||||
(!fileInfo.exists())
|
||||
? effectDefinition.script = ":/effects/"+scriptName.mid(1)
|
||||
: effectDefinition.script = scriptName;
|
||||
} else
|
||||
{
|
||||
(!fileInfo.exists())
|
||||
? effectDefinition.script = path + QDir::separator() + scriptName
|
||||
: effectDefinition.script = scriptName;
|
||||
}
|
||||
|
||||
effectDefinition.args = config["args"].toObject();
|
||||
effectDefinition.smoothCfg = 1; // pause config
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EffectFileHandler::loadEffectSchema(const QString &path, const QString &effectSchemaFile, EffectSchema & effectSchema)
|
||||
{
|
||||
QString fileName = path + "schema/" + QDir::separator() + effectSchemaFile;
|
||||
|
||||
// Read and parse the effect schema file
|
||||
QJsonObject schemaEffect;
|
||||
if(!JsonUtils::readFile(fileName, schemaEffect, _log))
|
||||
return false;
|
||||
|
||||
// setup the definition
|
||||
QString scriptName = schemaEffect["script"].toString();
|
||||
effectSchema.schemaFile = fileName;
|
||||
fileName = path + QDir::separator() + scriptName;
|
||||
QFile pyFile(fileName);
|
||||
|
||||
if (scriptName.isEmpty() || !pyFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
fileName = path + "schema/" + QDir::separator() + effectSchemaFile;
|
||||
Error( _log, "Python script '%s' in effect schema '%s' could not be loaded", QSTRING_CSTR(scriptName), QSTRING_CSTR(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
pyFile.close();
|
||||
|
||||
effectSchema.pyFile = (scriptName.mid(0, 1) == ":" ) ? ":/effects/"+scriptName.mid(1) : path + QDir::separator() + scriptName;
|
||||
effectSchema.pySchema = schemaEffect;
|
||||
|
||||
return true;
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
#include <cmath>
|
||||
|
||||
#include <effectengine/Effect.h>
|
||||
#include <effectengine/EffectModule.h>
|
||||
@ -44,10 +45,9 @@ PyObject *EffectModule::json2python(const QJsonValue &jsonData)
|
||||
return Py_BuildValue("");
|
||||
case QJsonValue::Double:
|
||||
{
|
||||
if (std::rint(jsonData.toDouble()) != jsonData.toDouble())
|
||||
{
|
||||
if (std::round(jsonData.toDouble()) != jsonData.toDouble())
|
||||
return Py_BuildValue("d", jsonData.toDouble());
|
||||
}
|
||||
|
||||
return Py_BuildValue("i", jsonData.toInt());
|
||||
}
|
||||
case QJsonValue::Bool:
|
||||
|
@ -7,6 +7,7 @@ CaptureCont::CaptureCont(Hyperion* hyperion)
|
||||
: QObject()
|
||||
, _hyperion(hyperion)
|
||||
, _systemCaptEnabled(false)
|
||||
, _systemInactiveTimer(new QTimer(this))
|
||||
, _v4lCaptEnabled(false)
|
||||
, _v4lInactiveTimer(new QTimer(this))
|
||||
{
|
||||
@ -16,6 +17,11 @@ CaptureCont::CaptureCont(Hyperion* hyperion)
|
||||
// comp changes
|
||||
connect(_hyperion, &Hyperion::componentStateChanged, this, &CaptureCont::componentStateChanged);
|
||||
|
||||
// inactive timer system
|
||||
connect(_systemInactiveTimer, &QTimer::timeout, this, &CaptureCont::setSystemInactive);
|
||||
_systemInactiveTimer->setSingleShot(true);
|
||||
_systemInactiveTimer->setInterval(5000);
|
||||
|
||||
// inactive timer v4l
|
||||
connect(_v4lInactiveTimer, &QTimer::timeout, this, &CaptureCont::setV4lInactive);
|
||||
_v4lInactiveTimer->setSingleShot(true);
|
||||
@ -38,6 +44,7 @@ void CaptureCont::handleV4lImage(const Image<ColorRgb> & image)
|
||||
|
||||
void CaptureCont::handleSystemImage(const Image<ColorRgb>& image)
|
||||
{
|
||||
_systemInactiveTimer->start();
|
||||
_hyperion->setInputImage(_systemCaptPrio, image);
|
||||
}
|
||||
|
||||
@ -118,3 +125,8 @@ void CaptureCont::setV4lInactive()
|
||||
{
|
||||
_hyperion->setInputInactive(_v4lCaptPrio);
|
||||
}
|
||||
|
||||
void CaptureCont::setSystemInactive()
|
||||
{
|
||||
_hyperion->setInputInactive(_systemCaptPrio);
|
||||
}
|
||||
|
@ -95,9 +95,7 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
, _ledBuffer(_ledString.leds().size(), ColorRgb::BLACK)
|
||||
{
|
||||
if (!_raw2ledAdjustment->verifyAdjustments())
|
||||
{
|
||||
Warning(_log, "At least one led has no color calibration, please add all leds from your led layout to an 'LED index' field!");
|
||||
}
|
||||
|
||||
// handle hwLedCount
|
||||
_hwLedCount = qMax(unsigned(getSetting(settings::DEVICE).object()["hardwareLedCount"].toInt(getLedCount())), getLedCount());
|
||||
@ -107,6 +105,7 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
{
|
||||
_ledStringColorOrder.push_back(led.colorOrder);
|
||||
}
|
||||
|
||||
for (Led& led : _ledStringClone.leds())
|
||||
{
|
||||
_ledStringColorOrder.insert(_ledStringColorOrder.begin() + led.index, led.colorOrder);
|
||||
@ -135,7 +134,7 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
getComponentRegister().componentStateChanged(hyperion::COMP_LEDDEVICE, _device->componentState());
|
||||
|
||||
// create the effect engine and pipe the updateEmit; must be initialized after smoothing!
|
||||
_effectEngine = new EffectEngine(this,getSetting(settings::EFFECTS).object());
|
||||
_effectEngine = new EffectEngine(this);
|
||||
connect(_effectEngine, &EffectEngine::effectListUpdated, this, &Hyperion::effectListUpdated);
|
||||
|
||||
// setup config state checks and initial shot
|
||||
@ -178,7 +177,6 @@ void Hyperion::freeObjects(bool emitCloseSignal)
|
||||
{
|
||||
// switch off all leds
|
||||
clearall(true);
|
||||
_device->switchOff();
|
||||
|
||||
if (emitCloseSignal)
|
||||
{
|
||||
@ -507,9 +505,14 @@ const Hyperion::InputInfo Hyperion::getPriorityInfo(const int priority) const
|
||||
return _muxer.getInputInfo(priority);
|
||||
}
|
||||
|
||||
void Hyperion::reloadEffects()
|
||||
const bool Hyperion::saveEffect(const QJsonObject& obj, QString& resultMsg)
|
||||
{
|
||||
_effectEngine->readEffects();
|
||||
return _effectEngine->saveEffect(obj, resultMsg);
|
||||
}
|
||||
|
||||
const bool Hyperion::deleteEffect(const QString& effectName, QString& resultMsg)
|
||||
{
|
||||
return _effectEngine->deleteEffect(effectName, resultMsg);
|
||||
}
|
||||
|
||||
const std::list<EffectDefinition> & Hyperion::getEffects() const
|
||||
@ -625,12 +628,14 @@ void Hyperion::update()
|
||||
{
|
||||
_ledBuffer = priorityInfo.ledColors;
|
||||
}
|
||||
// copy rawLedColors before adjustments
|
||||
_rawLedBuffer = _ledBuffer;
|
||||
|
||||
// emit rawLedColors before transform
|
||||
emit rawLedColors(_ledBuffer);
|
||||
|
||||
// apply adjustments
|
||||
if(compChanged)
|
||||
_raw2ledAdjustment->setBacklightEnabled((_prevCompId != hyperion::COMP_COLOR && _prevCompId != hyperion::COMP_EFFECT));
|
||||
|
||||
_raw2ledAdjustment->applyAdjustment(_ledBuffer);
|
||||
|
||||
// insert cloned leds into buffer
|
||||
|
@ -302,11 +302,10 @@ void PriorityMuxer::setCurrentTime(void)
|
||||
if(infoIt->timeoutTime_ms >= -1)
|
||||
newPriority = qMin(newPriority, infoIt->priority);
|
||||
|
||||
// call timeTrigger when effect or color is running with timeout > -1, blacklist prio 255
|
||||
if(infoIt->priority < 254 && infoIt->timeoutTime_ms > -1 && (infoIt->componentId == hyperion::COMP_EFFECT || infoIt->componentId == hyperion::COMP_COLOR))
|
||||
{
|
||||
// call timeTrigger when effect or color is running with timeout > 0, blacklist prio 255
|
||||
if(infoIt->priority < 254 && infoIt->timeoutTime_ms > 0 && (infoIt->componentId == hyperion::COMP_EFFECT || infoIt->componentId == hyperion::COMP_COLOR))
|
||||
emit signalTimeTrigger(); // as signal to prevent Threading issues
|
||||
}
|
||||
|
||||
++infoIt;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file alias="hyperion-icon.png">hyperion-icon_32px.png</file>
|
||||
<file alias="hyperion-schema">hyperion.schema.json</file>
|
||||
<file alias="hyperion_default.config">../../config/hyperion.config.json.default</file>
|
||||
<file alias="schema-general.json">schema/schema-general.json</file>
|
||||
|
@ -171,7 +171,7 @@ int LedDeviceAurora::write(const std::vector<ColorRgb> & ledValues)
|
||||
udpbuffer[i++] = panelCount;
|
||||
for (const ColorRgb& color : ledValues)
|
||||
{
|
||||
if (i<udpBufferSize) {
|
||||
if ((unsigned)i < udpBufferSize) {
|
||||
udpbuffer[i++] = panelIds[panelCounter++ % panelCount];
|
||||
udpbuffer[i++] = 1; // No of Frames
|
||||
udpbuffer[i++] = color.red;
|
||||
@ -180,7 +180,7 @@ int LedDeviceAurora::write(const std::vector<ColorRgb> & ledValues)
|
||||
udpbuffer[i++] = 0; // W not set manually
|
||||
udpbuffer[i++] = 1; // currently fixed at value 1 which corresponds to 100ms
|
||||
}
|
||||
if(panelCounter > panelCount) {
|
||||
if((unsigned)panelCounter > panelCount) {
|
||||
break;
|
||||
}
|
||||
//printf ("c.red %d sz c.red %d\n", color.red, sizeof(color.red));
|
||||
|
@ -88,7 +88,7 @@ const QString SSDPDiscover::getFirstService(const searchType& type, const QStrin
|
||||
//Info(_log, "Received msearch response from '%s:%d'. Search target: %s",QSTRING_CSTR(sender.toString()), senderPort, QSTRING_CSTR(headers.value("st")));
|
||||
if(type == STY_WEBSERVER)
|
||||
{
|
||||
Info(_log, "Found Hyperion server at: %s:%d", QSTRING_CSTR(url.host()), url.port());
|
||||
Info(_log, "Found Hyperion server at: %s:%s", QSTRING_CSTR(url.host()), url.port());
|
||||
|
||||
return url.host()+":"+QString::number(url.port());
|
||||
}
|
||||
@ -101,7 +101,7 @@ const QString SSDPDiscover::getFirstService(const searchType& type, const QStrin
|
||||
}
|
||||
else
|
||||
{
|
||||
Info(_log, "Found Hyperion server at: %s:%d", QSTRING_CSTR(url.host()), fbsport);
|
||||
Info(_log, "Found Hyperion server at: %s:%s", QSTRING_CSTR(url.host()), QSTRING_CSTR(fbsport));
|
||||
return url.host()+":"+fbsport;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
#include "QtHttpServer.h"
|
||||
#include "QtHttpRequest.h"
|
||||
#include "QtHttpReply.h"
|
||||
@ -9,95 +8,114 @@
|
||||
const QString & QtHttpServer::HTTP_VERSION = QStringLiteral ("HTTP/1.1");
|
||||
|
||||
QtHttpServerWrapper::QtHttpServerWrapper (QObject * parent)
|
||||
: QTcpServer (parent)
|
||||
, m_useSsl (false)
|
||||
{ }
|
||||
|
||||
QtHttpServerWrapper::~QtHttpServerWrapper (void) { }
|
||||
|
||||
void QtHttpServerWrapper::setUseSecure (const bool ssl) {
|
||||
m_useSsl = ssl;
|
||||
: QTcpServer (parent)
|
||||
, m_useSsl (false)
|
||||
{
|
||||
}
|
||||
|
||||
void QtHttpServerWrapper::incomingConnection (qintptr handle) {
|
||||
QTcpSocket * sock = (m_useSsl
|
||||
? new QSslSocket (this)
|
||||
: new QTcpSocket (this));
|
||||
if (sock->setSocketDescriptor (handle)) {
|
||||
addPendingConnection (sock);
|
||||
}
|
||||
else {
|
||||
delete sock;
|
||||
}
|
||||
QtHttpServerWrapper::~QtHttpServerWrapper (void)
|
||||
{
|
||||
}
|
||||
|
||||
void QtHttpServerWrapper::setUseSecure (const bool ssl) {
|
||||
m_useSsl = ssl;
|
||||
}
|
||||
|
||||
void QtHttpServerWrapper::incomingConnection (qintptr handle)
|
||||
{
|
||||
QTcpSocket * sock = (m_useSsl
|
||||
? new QSslSocket (this)
|
||||
: new QTcpSocket (this));
|
||||
(sock->setSocketDescriptor (handle))
|
||||
? addPendingConnection (sock)
|
||||
: delete sock;
|
||||
}
|
||||
|
||||
QtHttpServer::QtHttpServer (QObject * parent)
|
||||
: QObject (parent)
|
||||
, m_useSsl (false)
|
||||
, m_serverName (QStringLiteral ("The Qt5 HTTP Server"))
|
||||
: QObject (parent)
|
||||
, m_useSsl (false)
|
||||
, m_serverName (QStringLiteral ("The Qt5 HTTP Server"))
|
||||
{
|
||||
m_sockServer = new QtHttpServerWrapper (this);
|
||||
connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected);
|
||||
m_sockServer = new QtHttpServerWrapper (this);
|
||||
connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected);
|
||||
}
|
||||
|
||||
const QString & QtHttpServer::getServerName (void) const {
|
||||
return m_serverName;
|
||||
const QString & QtHttpServer::getServerName (void) const
|
||||
{
|
||||
return m_serverName;
|
||||
}
|
||||
|
||||
quint16 QtHttpServer::getServerPort (void) const {
|
||||
return m_sockServer->serverPort ();
|
||||
quint16 QtHttpServer::getServerPort (void) const
|
||||
{
|
||||
return m_sockServer->serverPort ();
|
||||
}
|
||||
|
||||
QString QtHttpServer::getErrorString (void) const {
|
||||
return m_sockServer->errorString ();
|
||||
QString QtHttpServer::getErrorString (void) const
|
||||
{
|
||||
return m_sockServer->errorString ();
|
||||
}
|
||||
|
||||
void QtHttpServer::start (quint16 port) {
|
||||
void QtHttpServer::start (quint16 port)
|
||||
{
|
||||
if(!m_sockServer->isListening())
|
||||
(m_sockServer->listen (QHostAddress::Any, port))
|
||||
? emit started (m_sockServer->serverPort ())
|
||||
: emit error (m_sockServer->errorString ());
|
||||
}
|
||||
|
||||
void QtHttpServer::stop (void)
|
||||
{
|
||||
if (m_sockServer->isListening ())
|
||||
{
|
||||
if (m_sockServer->listen (QHostAddress::Any, port)) {
|
||||
emit started (m_sockServer->serverPort ());
|
||||
}
|
||||
else {
|
||||
emit error (m_sockServer->errorString ());
|
||||
m_sockServer->close ();
|
||||
|
||||
// disconnect clients
|
||||
const QList<QTcpSocket*> socks = m_socksClientsHash.keys();
|
||||
for(auto sock : socks)
|
||||
{
|
||||
sock->close();
|
||||
}
|
||||
|
||||
emit stopped ();
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpServer::stop (void) {
|
||||
if (m_sockServer->isListening ()) {
|
||||
m_sockServer->close ();
|
||||
emit stopped ();
|
||||
}
|
||||
void QtHttpServer::setServerName (const QString & serverName)
|
||||
{
|
||||
m_serverName = serverName;
|
||||
}
|
||||
|
||||
void QtHttpServer::setServerName (const QString & serverName) {
|
||||
m_serverName = serverName;
|
||||
void QtHttpServer::setUseSecure (const bool ssl)
|
||||
{
|
||||
m_useSsl = ssl;
|
||||
m_sockServer->setUseSecure (m_useSsl);
|
||||
}
|
||||
|
||||
void QtHttpServer::setUseSecure (const bool ssl) {
|
||||
m_useSsl = ssl;
|
||||
m_sockServer->setUseSecure (m_useSsl);
|
||||
void QtHttpServer::setPrivateKey (const QSslKey & key)
|
||||
{
|
||||
m_sslKey = key;
|
||||
}
|
||||
|
||||
void QtHttpServer::setPrivateKey (const QSslKey & key) {
|
||||
m_sslKey = key;
|
||||
void QtHttpServer::setCertificates (const QList<QSslCertificate> & certs)
|
||||
{
|
||||
m_sslCerts = certs;
|
||||
}
|
||||
|
||||
void QtHttpServer::setCertificates (const QList<QSslCertificate> & certs) {
|
||||
m_sslCerts = certs;
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientConnected (void) {
|
||||
while (m_sockServer->hasPendingConnections ()) {
|
||||
if (QTcpSocket * sock = m_sockServer->nextPendingConnection ()) {
|
||||
void QtHttpServer::onClientConnected (void)
|
||||
{
|
||||
while (m_sockServer->hasPendingConnections ())
|
||||
{
|
||||
if (QTcpSocket * sock = m_sockServer->nextPendingConnection ())
|
||||
{
|
||||
connect (sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
|
||||
if (m_useSsl) {
|
||||
if (QSslSocket * ssl = qobject_cast<QSslSocket *> (sock)) {
|
||||
if (m_useSsl)
|
||||
{
|
||||
if (QSslSocket * ssl = qobject_cast<QSslSocket *> (sock))
|
||||
{
|
||||
connect (ssl, SslErrorSignal (&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors);
|
||||
connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
|
||||
connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
|
||||
connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
|
||||
connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
|
||||
connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
|
||||
connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
|
||||
ssl->setLocalCertificateChain (m_sslCerts);
|
||||
ssl->setPrivateKey (m_sslKey);
|
||||
ssl->setPeerVerifyMode (QSslSocket::AutoVerifyPeer);
|
||||
@ -107,30 +125,38 @@ void QtHttpServer::onClientConnected (void) {
|
||||
QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sock, this);
|
||||
m_socksClientsHash.insert (sock, wrapper);
|
||||
emit clientConnected (wrapper->getGuid ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientSslEncrypted (void) { }
|
||||
|
||||
void QtHttpServer::onClientSslPeerVerifyError (const QSslError & err) {
|
||||
Q_UNUSED (err)
|
||||
void QtHttpServer::onClientSslEncrypted (void)
|
||||
{
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientSslErrors (const QList<QSslError> & errors) {
|
||||
Q_UNUSED (errors)
|
||||
void QtHttpServer::onClientSslPeerVerifyError (const QSslError & err)
|
||||
{
|
||||
Q_UNUSED (err)
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientSslModeChanged (QSslSocket::SslMode mode) {
|
||||
Q_UNUSED (mode)
|
||||
void QtHttpServer::onClientSslErrors (const QList<QSslError> & errors)
|
||||
{
|
||||
Q_UNUSED (errors)
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientDisconnected (void) {
|
||||
if (QTcpSocket * sockClient = qobject_cast<QTcpSocket *> (sender ())) {
|
||||
if (QtHttpClientWrapper * wrapper = m_socksClientsHash.value (sockClient, Q_NULLPTR)) {
|
||||
emit clientDisconnected (wrapper->getGuid ());
|
||||
wrapper->deleteLater ();
|
||||
m_socksClientsHash.remove (sockClient);
|
||||
}
|
||||
}
|
||||
void QtHttpServer::onClientSslModeChanged (QSslSocket::SslMode mode)
|
||||
{
|
||||
Q_UNUSED (mode)
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientDisconnected (void)
|
||||
{
|
||||
if (QTcpSocket * sockClient = qobject_cast<QTcpSocket *> (sender ()))
|
||||
{
|
||||
if (QtHttpClientWrapper * wrapper = m_socksClientsHash.value (sockClient, Q_NULLPTR))
|
||||
{
|
||||
emit clientDisconnected (wrapper->getGuid ());
|
||||
wrapper->deleteLater ();
|
||||
m_socksClientsHash.remove (sockClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,11 @@
|
||||
|
||||
|
||||
WebServer::WebServer(const QJsonDocument& config, QObject * parent)
|
||||
: QObject(parent)
|
||||
: QObject(parent)
|
||||
, _config(config)
|
||||
, _log(Logger::getInstance("WEBSERVER"))
|
||||
, _server()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
WebServer::~WebServer()
|
||||
|
@ -227,8 +227,8 @@ void WebSocketClient::sendClose(int status, QString reason)
|
||||
|
||||
void WebSocketClient::handleBinaryMessage(QByteArray &data)
|
||||
{
|
||||
uint8_t priority = data.at(0);
|
||||
unsigned duration_s = data.at(1);
|
||||
//uint8_t priority = data.at(0);
|
||||
//unsigned duration_s = data.at(1);
|
||||
unsigned imgSize = data.size() - 4;
|
||||
unsigned width = ((data.at(2) << 8) & 0xFF00) | (data.at(3) & 0xFF);
|
||||
unsigned height = imgSize / width;
|
||||
@ -244,7 +244,7 @@ void WebSocketClient::handleBinaryMessage(QByteArray &data)
|
||||
|
||||
memcpy(image.memptr(), data.data()+4, imgSize);
|
||||
//_hyperion->registerInput();
|
||||
_hyperion->setInputImage(priority, image, duration_s*1000);
|
||||
//_hyperion->setInputImage(priority, image, duration_s*1000);
|
||||
}
|
||||
|
||||
qint64 WebSocketClient::sendMessage(QJsonObject obj)
|
||||
|
22
resources/CMakeLists.txt
Normal file
22
resources/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# Global resource file to embed static data into code, available as static lib 'resources'. File names needs to be unique in the /resources directory and below.
|
||||
# All files are available with their file name by calling ":/FILENAME". Probably you need to call Q_INIT_RESOURCE("resources") once
|
||||
#
|
||||
# Define the current source locations
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/resources)
|
||||
|
||||
# catch all files
|
||||
FILE ( GLOB Hyperion_RESFILES "${CURRENT_SOURCE_DIR}/icons/*" )
|
||||
|
||||
# fill resources.qrc with RESFILES
|
||||
FOREACH( f ${Hyperion_RESFILES} )
|
||||
get_filename_component(fname ${f} NAME)
|
||||
SET(HYPERION_RES "${HYPERION_RES}\n\t\t<file alias=\"${fname}\">${f}</file>")
|
||||
ENDFOREACH()
|
||||
|
||||
# prep file
|
||||
CONFIGURE_FILE(${CURRENT_SOURCE_DIR}/resources.qrc.in ${CMAKE_BINARY_DIR}/resources.qrc )
|
||||
SET(Hyperion_RES ${CMAKE_BINARY_DIR}/resources.qrc)
|
||||
|
||||
add_library(resources
|
||||
${Hyperion_RES}
|
||||
)
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
5
resources/resources.qrc.in
Normal file
5
resources/resources.qrc.in
Normal file
@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
${HYPERION_RES}
|
||||
</qresource>
|
||||
</RCC>
|
@ -1,7 +1,7 @@
|
||||
add_subdirectory(hyperiond)
|
||||
add_subdirectory(hyperion-remote)
|
||||
|
||||
# The following clients depend on the protobuf library
|
||||
# The following binaries are just compiled if requested
|
||||
if (ENABLE_AMLOGIC)
|
||||
add_subdirectory(hyperion-aml)
|
||||
endif()
|
||||
|
@ -23,9 +23,7 @@ add_executable(${PROJECT_NAME}
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
effectengine
|
||||
commandline
|
||||
blackborder
|
||||
hyperion-utils
|
||||
flatbufserver
|
||||
flatbuffers
|
||||
|
@ -37,7 +37,7 @@ int main(int argc, char ** argv)
|
||||
// create the option parser and initialize all parser
|
||||
Parser parser("AmLogic capture application for Hyperion. Will automatically search a Hyperion server if -a option isn't used. Please note that if you have more than one server running it's more or less random which one will be used.");
|
||||
|
||||
IntOption & argFps = parser.add<IntOption> ('f', "framerate", "Capture frame rate [default: %1]", "10");
|
||||
IntOption & argFps = parser.add<IntOption> ('f', "framerate", "Capture frame rate [default: %1]", "10", 1, 25);
|
||||
IntOption & argWidth = parser.add<IntOption> (0x0, "width", "Width of the captured image [default: %1]", "160", 160, 4096);
|
||||
IntOption & argHeight = parser.add<IntOption> (0x0, "height", "Height of the captured image [default: %1]", "160", 160, 4096);
|
||||
BooleanOption & argScreenshot = parser.add<BooleanOption>(0x0, "screenshot", "Take a single screenshot, save it to file and quit");
|
||||
|
@ -30,9 +30,7 @@ add_executable( ${PROJECT_NAME}
|
||||
)
|
||||
|
||||
target_link_libraries( ${PROJECT_NAME}
|
||||
effectengine
|
||||
commandline
|
||||
blackborder
|
||||
hyperion-utils
|
||||
flatbufserver
|
||||
flatbuffers
|
||||
|
@ -36,9 +36,9 @@ int main(int argc, char ** argv)
|
||||
// create the option parser and initialize all parameters
|
||||
Parser parser("Dispmanx capture application for Hyperion. Will automatically search a Hyperion server if -a option isn't used. Please note that if you have more than one server running it's more or less random which one will be used.");
|
||||
|
||||
IntOption & argFps = parser.add<IntOption> ('f', "framerate", "Capture frame rate [default: %1]", "10");
|
||||
IntOption & argWidth = parser.add<IntOption> (0x0, "width", "Width of the captured image [default: %1]", "64", 32, 4096);
|
||||
IntOption & argHeight = parser.add<IntOption> (0x0, "height", "Height of the captured image [default: %1]", "64", 32, 4096);
|
||||
IntOption & argFps = parser.add<IntOption> ('f', "framerate", "Capture frame rate [default: %1]", "10", 1, 25);
|
||||
IntOption & argWidth = parser.add<IntOption> (0x0, "width", "Width of the captured image [default: %1]", "64", 64);
|
||||
IntOption & argHeight = parser.add<IntOption> (0x0, "height", "Height of the captured image [default: %1]", "64", 64);
|
||||
|
||||
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");
|
||||
|
@ -23,9 +23,7 @@ add_executable( ${PROJECT_NAME}
|
||||
)
|
||||
|
||||
target_link_libraries( ${PROJECT_NAME}
|
||||
effectengine
|
||||
commandline
|
||||
blackborder
|
||||
hyperion-utils
|
||||
flatbufserver
|
||||
flatbuffers
|
||||
|
@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
// QT includes
|
||||
#include <QCoreApplication>
|
||||
#include <QImage>
|
||||
@ -31,9 +29,9 @@ int main(int argc, char ** argv)
|
||||
Parser parser("FrameBuffer capture application for Hyperion. Will automatically search a Hyperion server if -a option isn't used. Please note that if you have more than one server running it's more or less random which one will be used.");
|
||||
|
||||
Option & argDevice = parser.add<Option> ('d', "device", "Set the video device [default: %1]", "/dev/video0");
|
||||
IntOption & argFps = parser.add<IntOption> ('f', "framerate", "Capture frame rate [default: %1]", "10");
|
||||
IntOption & argWidth = parser.add<IntOption> (0x0, "width", "Width of the captured image [default: %1]", "160", 160, 4096);
|
||||
IntOption & argHeight = parser.add<IntOption> (0x0, "height", "Height of the captured image [default: %1]", "160", 160, 4096);
|
||||
IntOption & argFps = parser.add<IntOption> ('f', "framerate", "Capture frame rate [default: %1]", "10", 1, 25);
|
||||
IntOption & argWidth = parser.add<IntOption> (0x0, "width", "Width of the captured image [default: %1]", "160", 160);
|
||||
IntOption & argHeight = parser.add<IntOption> (0x0, "height", "Height of the captured image [default: %1]", "160", 160);
|
||||
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");
|
||||
|
@ -23,9 +23,7 @@ add_executable( ${PROJECT_NAME}
|
||||
)
|
||||
|
||||
target_link_libraries( ${PROJECT_NAME}
|
||||
effectengine
|
||||
commandline
|
||||
blackborder
|
||||
hyperion-utils
|
||||
flatbufserver
|
||||
flatbuffers
|
||||
|
@ -30,10 +30,10 @@ int main(int argc, char ** argv)
|
||||
// create the option parser and initialize all parameters
|
||||
Parser parser("OSX capture application for Hyperion. Will automatically search a Hyperion server if -a option isn't used. Please note that if you have more than one server running it's more or less random which one will be used.");
|
||||
|
||||
Option & argDisplay = parser.add<Option> ('d', "display", "Set the display to capture [default: %1]");
|
||||
IntOption & argFps = parser.add<IntOption> ('f', "framerate", "Capture frame rate [default: %1]", "10", 1, 600);
|
||||
IntOption & argWidth = parser.add<IntOption> (0x0, "width", "Width of the captured image [default: %1]", "160", 160, 4096);
|
||||
IntOption & argHeight = parser.add<IntOption> (0x0, "height", "Height of the captured image [default: %1]", "160", 160, 4096);
|
||||
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 & argWidth = parser.add<IntOption> (0x0, "width", "Width of the captured image [default: %1]", "160", 160);
|
||||
IntOption & argHeight = parser.add<IntOption> (0x0, "height", "Height of the captured image [default: %1]", "160", 160);
|
||||
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");
|
||||
|
@ -23,10 +23,8 @@ add_executable(${PROJECT_NAME}
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
effectengine
|
||||
v4l2-grabber
|
||||
commandline
|
||||
blackborder
|
||||
hyperion-utils
|
||||
flatbufserver
|
||||
flatbuffers
|
||||
|
@ -67,7 +67,7 @@ int main(int argc, char** argv)
|
||||
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 size [default=%1]", "1");
|
||||
IntOption & argSizeDecimation = parser.add<IntOption> ('s', "size-decimator", "Decimation factor for the output size [default=%1]", "6", 1);
|
||||
BooleanOption & argScreenshot = parser.add<BooleanOption>(0x0, "screenshot", "Take a single screenshot, save it to file and quit");
|
||||
|
||||
BooleanOption & argSignalDetection = parser.add<BooleanOption>('s', "signal-detection-disabled", "disable signal detection");
|
||||
|
@ -25,8 +25,6 @@ add_executable(${PROJECT_NAME}
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
effectengine
|
||||
blackborder
|
||||
commandline
|
||||
hyperion-utils
|
||||
flatbufserver
|
||||
|
@ -42,7 +42,7 @@ int main(int argc, char ** argv)
|
||||
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 size [default=%1]", "8");
|
||||
IntOption & argSizeDecimation = parser.add<IntOption> ('s', "size-decimator", "Decimation factor for the output 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");
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
find_package(PythonLibs 3.5 REQUIRED)
|
||||
include_directories(${PYTHON_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}/..)
|
||||
|
||||
@ -21,6 +20,7 @@ target_link_libraries(hyperiond
|
||||
bonjour
|
||||
ssdp
|
||||
python
|
||||
resources
|
||||
${PYTHON_LIBRARIES}
|
||||
)
|
||||
|
||||
@ -60,6 +60,7 @@ qt5_use_modules(hyperiond Core Gui Network Widgets)
|
||||
install ( TARGETS hyperiond DESTINATION "share/hyperion/bin/" COMPONENT "${PLATFORM}" )
|
||||
install ( DIRECTORY ${CMAKE_SOURCE_DIR}/bin/service DESTINATION "share/hyperion/" COMPONENT "${PLATFORM}" )
|
||||
install ( FILES ${CMAKE_SOURCE_DIR}/effects/readme.txt DESTINATION "share/hyperion/effects" COMPONENT "${PLATFORM}" )
|
||||
install ( FILES ${CMAKE_SOURCE_DIR}/resources/icons/hyperion-icon-32px.png DESTINATION "share/hyperion/icons" COMPONENT "${PLATFORM}" )
|
||||
|
||||
if(CMAKE_HOST_UNIX)
|
||||
install(CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/hyperiond\" \"${CMAKE_BINARY_DIR}/symlink_hyperiond\" )" COMPONENT "${PLATFORM}" )
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <QResource>
|
||||
#include <QLocale>
|
||||
#include <QFile>
|
||||
#include <QHostAddress>
|
||||
#include <QString>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
@ -42,6 +41,9 @@
|
||||
// Init Python
|
||||
#include <python/PythonInit.h>
|
||||
|
||||
// EffectFileHandler
|
||||
#include <effectengine/EffectFileHandler.h>
|
||||
|
||||
HyperionDaemon* HyperionDaemon::daemon = nullptr;
|
||||
|
||||
HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObject *parent, const bool& logLvlOverwrite)
|
||||
@ -79,6 +81,10 @@ HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObje
|
||||
if(!logLvlOverwrite)
|
||||
handleSettingsUpdate(settings::LOGGER, _settingsManager->getSetting(settings::LOGGER));
|
||||
|
||||
// init EffectFileHandler
|
||||
EffectFileHandler* efh = new EffectFileHandler(rootPath, _settingsManager->getSetting(settings::EFFECTS), this);
|
||||
connect(this, &HyperionDaemon::settingsChanged, efh, &EffectFileHandler::handleSettingsUpdate);
|
||||
|
||||
// spawn all Hyperion instances before network services
|
||||
_hyperion = Hyperion::initInstance(this, 0, configFile, rootPath);
|
||||
|
||||
@ -201,7 +207,7 @@ void HyperionDaemon::startNetworkServices()
|
||||
connect(this, &HyperionDaemon::settingsChanged, _webserver, &WebServer::handleSettingsUpdate);
|
||||
wsThread->start();
|
||||
|
||||
// create SSDPHandler in thread
|
||||
// create ssdp server in thread
|
||||
_ssdp = new SSDPHandler(_webserver, getSetting(settings::FLATBUFSERVER).object()["port"].toInt());
|
||||
QThread* ssdpThread = new QThread(this);
|
||||
_ssdp->moveToThread(ssdpThread);
|
||||
@ -268,15 +274,72 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type& type, const QJso
|
||||
{
|
||||
type = "framebuffer";
|
||||
}
|
||||
Info( _log, "set screen capture device to '%s'", QSTRING_CSTR(type));
|
||||
}
|
||||
|
||||
if (type == "") { Info( _log, "screen capture device disabled"); }
|
||||
else if (type == "framebuffer" && _fbGrabber == nullptr) createGrabberFramebuffer(grabberConfig);
|
||||
else if (type == "dispmanx" && _dispmanx == nullptr) createGrabberDispmanx();
|
||||
else if (type == "amlogic" && _amlGrabber == nullptr) createGrabberAmlogic();
|
||||
else if (type == "osx" && _osxGrabber == nullptr) createGrabberOsx(grabberConfig);
|
||||
else if (type == "x11" && _x11Grabber == nullptr) createGrabberX11(grabberConfig);
|
||||
if(_prevType != type)
|
||||
{
|
||||
Info( _log, "set screen capture device to '%s'", QSTRING_CSTR(type));
|
||||
|
||||
// stop all capture interfaces
|
||||
#ifdef ENABLE_FB
|
||||
if(_fbGrabber != nullptr) _fbGrabber->stop();
|
||||
#endif
|
||||
#ifdef ENABLE_DISPMANX
|
||||
if(_dispmanx != nullptr) _dispmanx->stop();
|
||||
#endif
|
||||
#ifdef ENABLE_AMLOGIC
|
||||
if(_amlGrabber != nullptr) _amlGrabber->stop();
|
||||
#endif
|
||||
#ifdef ENABLE_OSX
|
||||
if(_osxGrabber != nullptr) _osxGrabber->stop();
|
||||
#endif
|
||||
#ifdef ENABLE_X11
|
||||
if(_x11Grabber != nullptr) _x11Grabber->stop();
|
||||
#endif
|
||||
|
||||
// create/start capture interface
|
||||
if(type == "framebuffer")
|
||||
{
|
||||
if(_fbGrabber == nullptr)
|
||||
createGrabberFramebuffer(grabberConfig);
|
||||
#ifdef ENABLE_FB
|
||||
_fbGrabber->start();
|
||||
#endif
|
||||
}
|
||||
else if(type == "dispmanx")
|
||||
{
|
||||
if(_dispmanx == nullptr)
|
||||
createGrabberDispmanx();
|
||||
#ifdef ENABLE_DISPMANX
|
||||
_dispmanx->start();
|
||||
#endif
|
||||
}
|
||||
else if(type == "amlogic")
|
||||
{
|
||||
if(_amlGrabber == nullptr)
|
||||
createGrabberAmlogic();
|
||||
#ifdef ENABLE_AMLOGIC
|
||||
_amlGrabber->start();
|
||||
#endif
|
||||
}
|
||||
else if(type == "osx")
|
||||
{
|
||||
if(_osxGrabber == nullptr)
|
||||
createGrabberOsx(grabberConfig);
|
||||
#ifdef ENABLE_OSX
|
||||
_osxGrabber->start();
|
||||
#endif
|
||||
}
|
||||
else if(type == "x11")
|
||||
{
|
||||
if(_x11Grabber == nullptr)
|
||||
createGrabberX11(grabberConfig);
|
||||
#ifdef ENABLE_X11
|
||||
_x11Grabber->start();
|
||||
#endif
|
||||
}
|
||||
_prevType = type;
|
||||
}
|
||||
}
|
||||
else if(type == settings::V4L2)
|
||||
{
|
||||
@ -341,7 +404,7 @@ void HyperionDaemon::createGrabberDispmanx()
|
||||
|
||||
_dispmanx->start();
|
||||
|
||||
Info(_log, "DISPMANX frame grabber created and started");
|
||||
Info(_log, "DISPMANX frame grabber created");
|
||||
#else
|
||||
Error( _log, "The dispmanx framegrabber can not be instantiated, because it has been left out from the build");
|
||||
#endif
|
||||
@ -360,7 +423,7 @@ void HyperionDaemon::createGrabberAmlogic()
|
||||
connect(this, &HyperionDaemon::settingsChanged, _amlGrabber, &AmlogicWrapper::handleSettingsUpdate);
|
||||
|
||||
_amlGrabber->start();
|
||||
Info(_log, "AMLOGIC grabber created and started");
|
||||
Info(_log, "AMLOGIC grabber created");
|
||||
#else
|
||||
Error( _log, "The AMLOGIC grabber can not be instantiated, because it has been left out from the build");
|
||||
#endif
|
||||
@ -381,7 +444,7 @@ void HyperionDaemon::createGrabberX11(const QJsonObject & grabberConfig)
|
||||
connect(this, &HyperionDaemon::settingsChanged, _x11Grabber, &X11Wrapper::handleSettingsUpdate);
|
||||
|
||||
_x11Grabber->start();
|
||||
Info(_log, "X11 grabber created and started");
|
||||
Info(_log, "X11 grabber created");
|
||||
#else
|
||||
Error(_log, "The X11 grabber can not be instantiated, because it has been left out from the build");
|
||||
#endif
|
||||
@ -402,7 +465,7 @@ void HyperionDaemon::createGrabberFramebuffer(const QJsonObject & grabberConfig)
|
||||
connect(this, &HyperionDaemon::settingsChanged, _fbGrabber, &FramebufferWrapper::handleSettingsUpdate);
|
||||
|
||||
_fbGrabber->start();
|
||||
Info(_log, "Framebuffer grabber created and started");
|
||||
Info(_log, "Framebuffer grabber created");
|
||||
#else
|
||||
Error(_log, "The framebuffer grabber can not be instantiated, because it has been left out from the build");
|
||||
#endif
|
||||
@ -423,7 +486,7 @@ void HyperionDaemon::createGrabberOsx(const QJsonObject & grabberConfig)
|
||||
connect(this, &HyperionDaemon::settingsChanged, _osxGrabber, &OsxWrapper::handleSettingsUpdate);
|
||||
|
||||
_osxGrabber->start();
|
||||
Info(_log, "OSX grabber created and started");
|
||||
Info(_log, "OSX grabber created");
|
||||
#else
|
||||
Error(_log, "The osx grabber can not be instantiated, because it has been left out from the build");
|
||||
#endif
|
||||
|
@ -156,7 +156,9 @@ private:
|
||||
unsigned _grabber_cropLeft;
|
||||
unsigned _grabber_cropRight;
|
||||
unsigned _grabber_cropTop;
|
||||
unsigned _grabber_cropBottom;
|
||||
unsigned _grabber_cropBottom;
|
||||
|
||||
QString _prevType;
|
||||
|
||||
VideoMode _currVideoMode;
|
||||
SettingsManager* _settingsManager;
|
||||
|
@ -104,6 +104,7 @@ QCoreApplication* createApplication(int &argc, char *argv[])
|
||||
{
|
||||
QApplication* app = new QApplication(argc, argv);
|
||||
app->setApplicationDisplayName("Hyperion");
|
||||
app->setWindowIcon(QIcon(":/hyperion-icon-32px.png"));
|
||||
return app;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ SysTray::SysTray(HyperionDaemon *hyperiond)
|
||||
, _hyperion(nullptr)
|
||||
, _webPort(8090)
|
||||
{
|
||||
Q_INIT_RESOURCE(resource);
|
||||
Q_INIT_RESOURCE(resources);
|
||||
|
||||
// webserver port
|
||||
WebServer* webserver = _hyperiond->getWebServerInstance();
|
||||
@ -33,7 +33,7 @@ SysTray::SysTray(HyperionDaemon *hyperiond)
|
||||
|
||||
connect(_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
|
||||
connect(&_colorDlg, SIGNAL(currentColorChanged(const QColor&)), this, SLOT(setColor(const QColor &)));
|
||||
QIcon icon(":/hyperion-icon.png");
|
||||
QIcon icon(":/hyperion-icon-32px.png");
|
||||
_trayIcon->setIcon(icon);
|
||||
_trayIcon->show();
|
||||
setWindowIcon(icon);
|
||||
|
Loading…
Reference in New Issue
Block a user