This commit is contained in:
Paulchen-Panther 2018-12-31 15:48:29 +01:00
parent 0e3ddb7eca
commit d6b2cfaf9d
49 changed files with 899 additions and 535 deletions

View File

@ -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

View File

@ -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}" )

View File

@ -0,0 +1,5 @@
#!/bin/sh
cd "$(dirname "$0")"
# Path to hyperiond!?
cd ../Resources/bin
exec ./hyperiond "$@"

View File

@ -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 )

View File

@ -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>

View File

@ -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
///

View File

@ -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;
};

View 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;
};

View File

@ -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;

View File

@ -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;

View File

@ -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;
};

View File

@ -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();

View File

@ -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;

View File

@ -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();
}

View 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;
}

View File

@ -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:

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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>

View 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));

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -13,12 +13,11 @@
WebServer::WebServer(const QJsonDocument& config, QObject * parent)
: QObject(parent)
: QObject(parent)
, _config(config)
, _log(Logger::getInstance("WEBSERVER"))
, _server()
{
}
WebServer::~WebServer()

View File

@ -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
View 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}
)

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
${HYPERION_RES}
</qresource>
</RCC>

View File

@ -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()

View File

@ -23,9 +23,7 @@ add_executable(${PROJECT_NAME}
)
target_link_libraries(${PROJECT_NAME}
effectengine
commandline
blackborder
hyperion-utils
flatbufserver
flatbuffers

View File

@ -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");

View File

@ -30,9 +30,7 @@ add_executable( ${PROJECT_NAME}
)
target_link_libraries( ${PROJECT_NAME}
effectengine
commandline
blackborder
hyperion-utils
flatbufserver
flatbuffers

View File

@ -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");

View File

@ -23,9 +23,7 @@ add_executable( ${PROJECT_NAME}
)
target_link_libraries( ${PROJECT_NAME}
effectengine
commandline
blackborder
hyperion-utils
flatbufserver
flatbuffers

View File

@ -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");

View File

@ -23,9 +23,7 @@ add_executable( ${PROJECT_NAME}
)
target_link_libraries( ${PROJECT_NAME}
effectengine
commandline
blackborder
hyperion-utils
flatbufserver
flatbuffers

View File

@ -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");

View File

@ -23,10 +23,8 @@ add_executable(${PROJECT_NAME}
)
target_link_libraries(${PROJECT_NAME}
effectengine
v4l2-grabber
commandline
blackborder
hyperion-utils
flatbufserver
flatbuffers

View File

@ -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");

View File

@ -25,8 +25,6 @@ add_executable(${PROJECT_NAME}
)
target_link_libraries(${PROJECT_NAME}
effectengine
blackborder
commandline
hyperion-utils
flatbufserver

View File

@ -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");

View File

@ -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}" )

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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);