mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
hyperiond desktop integration (#453)
* add deployment * add correct api key * fix cmake lists and add heroku app name * Update .gitmodules sync modules with upstream * add possibility to start hyperiond as systray app * cleanup * - new command line options: --desktop --service to set desired mode (systray icon / console only) - auto detect x server - if avail run in gui mode - on osx always run in gui mode - use existing icon from webconfig, instead of own icon - add ability to no gice a config file name. If config not given, default config file will be set (home dir, or hyperiond dir, depending on writable state) * fix warnings and compile error * use own icon for systray purpose * use new logo * - set application properties - fix force service mode
This commit is contained in:
parent
5c7085439b
commit
6625a318ac
@ -147,8 +147,8 @@ private:
|
||||
double _y_frac_min;
|
||||
double _x_frac_max;
|
||||
double _y_frac_max;
|
||||
|
||||
int _currentFrame;
|
||||
|
||||
QSocketNotifier * _streamNotifier;
|
||||
|
||||
ImageResampler _imageResampler;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <utils/ColorRgb.h>
|
||||
|
||||
|
||||
|
@ -18,6 +18,8 @@ public:
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
quint16 getPort() { return _port; };
|
||||
|
||||
private:
|
||||
Hyperion* _hyperion;
|
||||
QString _baseUrl;
|
||||
|
@ -49,12 +49,12 @@ V4L2Grabber::V4L2Grabber(const QString & device
|
||||
, _noSignalThresholdColor(ColorRgb{0,0,0})
|
||||
, _signalDetectionEnabled(true)
|
||||
, _noSignalDetected(false)
|
||||
, _noSignalCounter(0)
|
||||
, _x_frac_min(0.25)
|
||||
, _y_frac_min(0.25)
|
||||
, _x_frac_max(0.75)
|
||||
, _y_frac_max(0.75)
|
||||
, _currentFrame(0)
|
||||
, _noSignalCounter(0)
|
||||
, _streamNotifier(nullptr)
|
||||
, _imageResampler()
|
||||
, _log(Logger::getInstance("V4L2:"+device))
|
||||
|
BIN
libsrc/hyperion/hyperion-icon_32px.png
Normal file
BIN
libsrc/hyperion/hyperion-icon_32px.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
@ -1,5 +1,6 @@
|
||||
<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>
|
||||
|
@ -1,7 +1,9 @@
|
||||
|
||||
add_executable(hyperiond
|
||||
hyperiond.h
|
||||
systray.h
|
||||
hyperiond.cpp
|
||||
systray.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
@ -42,6 +44,8 @@ if (ENABLE_X11)
|
||||
target_link_libraries(hyperiond x11-grabber )
|
||||
endif ()
|
||||
|
||||
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}" )
|
||||
|
@ -137,7 +137,7 @@ void HyperionDaemon::run()
|
||||
|
||||
void HyperionDaemon::loadConfig(const QString & configFile)
|
||||
{
|
||||
Info(_log, "Selected configuration file: %s", configFile.toUtf8().constData());
|
||||
Info(_log, "Selected configuration file: %s", QSTRING_CSTR(configFile));
|
||||
|
||||
// make sure the resources are loaded (they may be left out after static linking)
|
||||
Q_INIT_RESOURCE(resource);
|
||||
@ -224,7 +224,7 @@ void HyperionDaemon::startInitialEffect()
|
||||
else
|
||||
{
|
||||
int result = hyperion->setEffect(fgEffectConfig, FG_PRIORITY, fg_duration_ms);
|
||||
Info(_log,"Inital foreground effect '%s' %s", fgEffectConfig.toUtf8().constData(), ((result == 0) ? "started" : "failed"));
|
||||
Info(_log,"Inital foreground effect '%s' %s", QSTRING_CSTR(fgEffectConfig), ((result == 0) ? "started" : "failed"));
|
||||
}
|
||||
}
|
||||
// initial background effect/color
|
||||
@ -246,7 +246,7 @@ void HyperionDaemon::startInitialEffect()
|
||||
else
|
||||
{
|
||||
int result = hyperion->setEffect(bgEffectConfig, BG_PRIORITY, DURATION_INFINITY);
|
||||
Info(_log,"Inital background effect '%s' %s", bgEffectConfig.toUtf8().constData(), ((result == 0) ? "started" : "failed"));
|
||||
Info(_log,"Inital background effect '%s' %s", QSTRING_CSTR(bgEffectConfig), ((result == 0) ? "started" : "failed"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -435,7 +435,7 @@ void HyperionDaemon::createSystemFrameGrabber()
|
||||
{
|
||||
type = "framebuffer";
|
||||
}
|
||||
Info( _log, "set screen capture device to '%s'", type.toUtf8().constData());
|
||||
Info( _log, "set screen capture device to '%s'", QSTRING_CSTR(type));
|
||||
}
|
||||
|
||||
bool grabberCompState = grabberConfig["enable"].toBool(true);
|
||||
@ -445,7 +445,7 @@ void HyperionDaemon::createSystemFrameGrabber()
|
||||
else if (type == "amlogic") { createGrabberAmlogic(); createGrabberFramebuffer(grabberConfig); }
|
||||
else if (type == "osx") createGrabberOsx(grabberConfig);
|
||||
else if (type == "x11") createGrabberX11(grabberConfig);
|
||||
else { Warning( _log, "unknown framegrabber type '%s'", type.toUtf8().constData()); grabberCompState = false; }
|
||||
else { Warning( _log, "unknown framegrabber type '%s'", QSTRING_CSTR(type)); grabberCompState = false; }
|
||||
|
||||
// _hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_GRABBER, grabberCompState);
|
||||
_hyperion->setComponentState(hyperion::COMP_GRABBER, grabberCompState );
|
||||
|
@ -48,9 +48,14 @@
|
||||
#include <utils/Stats.h>
|
||||
#include <QJsonObject>
|
||||
|
||||
class SysTray;
|
||||
|
||||
class HyperionDaemon : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend SysTray;
|
||||
|
||||
public:
|
||||
HyperionDaemon(QString configFile, QObject *parent=nullptr);
|
||||
~HyperionDaemon();
|
||||
|
@ -11,12 +11,14 @@
|
||||
#include <exception>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QApplication>
|
||||
#include <QLocale>
|
||||
#include <QFile>
|
||||
#include <QString>
|
||||
#include <QResource>
|
||||
#include <QDir>
|
||||
#include <QStringList>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include "HyperionConfig.h"
|
||||
|
||||
@ -26,7 +28,12 @@
|
||||
#include <commandline/Parser.h>
|
||||
#include <commandline/IntOption.h>
|
||||
|
||||
#ifdef ENABLE_X11
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
#include "hyperiond.h"
|
||||
#include "systray.h"
|
||||
|
||||
using namespace commandline;
|
||||
|
||||
@ -58,6 +65,54 @@ void startNewHyperion(int parentPid, std::string hyperionFile, std::string confi
|
||||
}
|
||||
}
|
||||
|
||||
QCoreApplication* createApplication(int &argc, char *argv[])
|
||||
{
|
||||
bool isGuiApp = false;
|
||||
bool forceNoGui = false;
|
||||
// command line
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
if (qstrcmp(argv[i], "--desktop") == 0)
|
||||
{
|
||||
isGuiApp = true;
|
||||
}
|
||||
else if (qstrcmp(argv[i], "--service") == 0)
|
||||
{
|
||||
isGuiApp = false;
|
||||
forceNoGui = true;
|
||||
}
|
||||
}
|
||||
|
||||
// on osx/windows gui always available
|
||||
#if defined(__APPLE__) || defined(__WIN32__)
|
||||
isGuiApp = true && ! forceNoGui;
|
||||
#else
|
||||
if (!forceNoGui)
|
||||
{
|
||||
// if x11, then test if xserver is available
|
||||
#ifdef ENABLE_X11
|
||||
Display* dpy = XOpenDisplay(NULL);
|
||||
if (dpy != NULL)
|
||||
{
|
||||
XCloseDisplay(dpy);
|
||||
isGuiApp = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (isGuiApp)
|
||||
{
|
||||
QApplication* app = new QApplication(argc, argv);
|
||||
app->setApplicationDisplayName("Hyperion");
|
||||
return app;
|
||||
}
|
||||
|
||||
QCoreApplication* app = new QCoreApplication(argc, argv);
|
||||
app->setApplicationName("Hyperion");
|
||||
app->setApplicationVersion(HYPERION_VERSION);
|
||||
return app;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
@ -68,7 +123,8 @@ int main(int argc, char** argv)
|
||||
Logger::setLogLevel(Logger::WARNING);
|
||||
|
||||
// Initialising QCoreApplication
|
||||
QCoreApplication app(argc, argv);
|
||||
QScopedPointer<QCoreApplication> app(createApplication(argc, argv));
|
||||
bool isGuiApp = (qobject_cast<QApplication *>(app.data()) != 0 && QSystemTrayIcon::isSystemTrayAvailable());
|
||||
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
@ -88,14 +144,16 @@ int main(int argc, char** argv)
|
||||
BooleanOption & silentOption = parser.add<BooleanOption>('s', "silent", "do not print any outputs");
|
||||
BooleanOption & verboseOption = parser.add<BooleanOption>('v', "verbose", "Increase verbosity");
|
||||
BooleanOption & debugOption = parser.add<BooleanOption>('d', "debug", "Show debug messages");
|
||||
parser.add<BooleanOption>(0x0, "desktop", "show systray on desktop");
|
||||
parser.add<BooleanOption>(0x0, "service", "force hyperion to start as console service");
|
||||
Option & exportConfigOption = parser.add<Option> (0x0, "export-config", "export default config to file");
|
||||
Option & exportEfxOption = parser.add<Option> (0x0, "export-effects", "export effects to given path");
|
||||
|
||||
parser.addPositionalArgument("config-files", QCoreApplication::translate("main", "Configuration file"), "config.file");
|
||||
|
||||
parser.process(app);
|
||||
parser.process(*qApp);
|
||||
|
||||
const QStringList configFiles = parser.positionalArguments();
|
||||
QStringList configFiles = parser.positionalArguments();
|
||||
|
||||
int logLevelCheck = 0;
|
||||
if (parser.isSet(silentOption))
|
||||
@ -169,17 +227,33 @@ int main(int argc, char** argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// handle default config file
|
||||
if (configFiles.size() == 0)
|
||||
{
|
||||
QString hyperiond_path = QDir::homePath();
|
||||
QString hyperiond_config = hyperiond_path+"/.hyperion.config.json";
|
||||
QFileInfo hyperiond_pathinfo(hyperiond_path);
|
||||
|
||||
if ( ! hyperiond_pathinfo.isWritable() && ! QFile::exists(hyperiond_config) )
|
||||
{
|
||||
QFileInfo hyperiond_fileinfo(argv[0]);
|
||||
hyperiond_config = hyperiond_fileinfo.absolutePath()+"/hyperion.config.json";
|
||||
}
|
||||
|
||||
configFiles.append(hyperiond_config);
|
||||
Info(log, "No config file given. Standard config file used: %s", QSTRING_CSTR(configFiles[0]));
|
||||
}
|
||||
|
||||
bool exportDefaultConfig = false;
|
||||
bool exitAfterexportDefaultConfig = false;
|
||||
bool exitAfterExportDefaultConfig = false;
|
||||
QString exportConfigFileTarget;
|
||||
if (parser.isSet(exportConfigOption))
|
||||
{
|
||||
exportDefaultConfig = true;
|
||||
exitAfterexportDefaultConfig = true;
|
||||
exitAfterExportDefaultConfig = true;
|
||||
exportConfigFileTarget = exportConfigOption.value(parser);
|
||||
}
|
||||
else if ( configFiles.size() > 0 && ! QFile::exists(configFiles[0]) )
|
||||
else if ( ! QFile::exists(configFiles[0]) )
|
||||
{
|
||||
exportDefaultConfig = true;
|
||||
exportConfigFileTarget = configFiles[0];
|
||||
@ -194,21 +268,15 @@ int main(int argc, char** argv)
|
||||
{
|
||||
QFile::setPermissions(exportConfigFileTarget, PERM0664 );
|
||||
Info(log, "export complete.");
|
||||
if (exitAfterexportDefaultConfig) return 0;
|
||||
if (exitAfterExportDefaultConfig) return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(log, "error while export to %s",exportConfigFileTarget.toLocal8Bit().constData());
|
||||
if (exitAfterexportDefaultConfig) return 1;
|
||||
Error(log, "error while export to %s", QSTRING_CSTR(exportConfigFileTarget) );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (configFiles.size() == 0)
|
||||
{
|
||||
Error(log, "Missing required configuration file. Usage: hyperiond <options ...> config.file");
|
||||
parser.showHelp(0);
|
||||
return 1;
|
||||
}
|
||||
if (configFiles.size() > 1)
|
||||
{
|
||||
Warning(log, "You provided more than one config file. Hyperion will use only the first one");
|
||||
@ -223,10 +291,11 @@ int main(int argc, char** argv)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
HyperionDaemon* hyperiond = nullptr;
|
||||
try
|
||||
{
|
||||
hyperiond = new HyperionDaemon(configFiles[0], &app);
|
||||
hyperiond = new HyperionDaemon(configFiles[0], qApp);
|
||||
hyperiond->run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
@ -238,10 +307,21 @@ int main(int argc, char** argv)
|
||||
WebConfig* webConfig = nullptr;
|
||||
try
|
||||
{
|
||||
webConfig = new WebConfig(&app);
|
||||
webConfig = new WebConfig(qApp);
|
||||
// run the application
|
||||
rc = app.exec();
|
||||
Info(log, "INFO: Application closed with code %d", rc);
|
||||
if (isGuiApp)
|
||||
{
|
||||
Info(log, "start systray");
|
||||
QApplication::setQuitOnLastWindowClosed(false);
|
||||
SysTray tray(hyperiond, webConfig->getPort());
|
||||
tray.hide();
|
||||
rc = (qobject_cast<QApplication *>(app.data()))->exec();
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = app->exec();
|
||||
}
|
||||
Info(log, "Application closed with code %d", rc);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
136
src/hyperiond/systray.cpp
Normal file
136
src/hyperiond/systray.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QWindow>
|
||||
#include <QGuiApplication>
|
||||
#include <QWidget>
|
||||
#include <QColor>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include <utils/ColorRgb.h>
|
||||
#include <effectengine/EffectDefinition.h>
|
||||
|
||||
#include "hyperiond.h"
|
||||
#include "systray.h"
|
||||
|
||||
SysTray::SysTray(HyperionDaemon *hyperiond, quint16 webPort)
|
||||
: QWidget()
|
||||
, _colorDlg(this)
|
||||
, _hyperiond(hyperiond)
|
||||
, _webPort(webPort)
|
||||
{
|
||||
Q_INIT_RESOURCE(resource);
|
||||
_hyperion = Hyperion::getInstance();
|
||||
createTrayIcon();
|
||||
|
||||
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");
|
||||
_trayIcon->setIcon(icon);
|
||||
_trayIcon->show();
|
||||
setWindowIcon(icon);
|
||||
_colorDlg.setModal(true);
|
||||
_colorDlg.setOptions(QColorDialog::NoButtons);
|
||||
}
|
||||
|
||||
SysTray::~SysTray()
|
||||
{
|
||||
}
|
||||
|
||||
void SysTray::iconActivated(QSystemTrayIcon::ActivationReason reason)
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
case QSystemTrayIcon::Trigger:
|
||||
break;
|
||||
case QSystemTrayIcon::DoubleClick:
|
||||
showColorDialog();
|
||||
break;
|
||||
case QSystemTrayIcon::MiddleClick:
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
void SysTray::createTrayIcon()
|
||||
{
|
||||
quitAction = new QAction(tr("&Quit"), this);
|
||||
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
|
||||
|
||||
colorAction = new QAction(tr("&Color"), this);
|
||||
connect(colorAction, SIGNAL(triggered()), this, SLOT(showColorDialog()));
|
||||
|
||||
settingsAction = new QAction(tr("&Settings"), this);
|
||||
connect(settingsAction, SIGNAL(triggered()), this, SLOT(settings()));
|
||||
|
||||
clearAction = new QAction(tr("&Clear"), this);
|
||||
connect(clearAction, SIGNAL(triggered()), this, SLOT(clearEfxColor()));
|
||||
|
||||
const std::list<EffectDefinition> efxs = _hyperion->getEffects();
|
||||
_trayIconMenu = new QMenu(this);
|
||||
_trayIconEfxMenu = new QMenu(_trayIconMenu);
|
||||
_trayIconEfxMenu->setTitle(tr("Effects"));
|
||||
for (auto efx : efxs)
|
||||
{
|
||||
QAction *efxAction = new QAction(efx.name, this);
|
||||
connect(efxAction, SIGNAL(triggered()), this, SLOT(setEffect()));
|
||||
_trayIconEfxMenu->addAction(efxAction);
|
||||
}
|
||||
|
||||
_trayIconMenu->addAction(settingsAction);
|
||||
_trayIconMenu->addSeparator();
|
||||
_trayIconMenu->addAction(colorAction);
|
||||
_trayIconMenu->addMenu(_trayIconEfxMenu);
|
||||
_trayIconMenu->addAction(clearAction);
|
||||
_trayIconMenu->addSeparator();
|
||||
_trayIconMenu->addAction(quitAction);
|
||||
|
||||
_trayIcon = new QSystemTrayIcon(this);
|
||||
_trayIcon->setContextMenu(_trayIconMenu);
|
||||
}
|
||||
|
||||
void SysTray::setColor(const QColor & color)
|
||||
{
|
||||
ColorRgb rgbColor;
|
||||
rgbColor.red = color.red();
|
||||
rgbColor.green = color.green();
|
||||
rgbColor.blue =color.blue();
|
||||
|
||||
_hyperion->setColor(1 ,rgbColor, 0);
|
||||
}
|
||||
|
||||
void SysTray::showColorDialog()
|
||||
{
|
||||
if(_colorDlg.isVisible())
|
||||
{
|
||||
_colorDlg.hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
_colorDlg.show();
|
||||
}
|
||||
}
|
||||
|
||||
void SysTray::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void SysTray::settings()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("http://localhost:"+QString::number(_webPort)+"/", QUrl::TolerantMode));
|
||||
}
|
||||
|
||||
void SysTray::setEffect()
|
||||
{
|
||||
QString efxName = qobject_cast<QAction*>(sender())->text();
|
||||
_hyperion->setEffect(efxName, 1);
|
||||
}
|
||||
|
||||
void SysTray::clearEfxColor()
|
||||
{
|
||||
_hyperion->clear(1);
|
||||
}
|
50
src/hyperiond/systray.h
Normal file
50
src/hyperiond/systray.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QMenu>
|
||||
#include <QWidget>
|
||||
#include <QColorDialog>
|
||||
#include <QCloseEvent>
|
||||
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
class HyperionDaemon;
|
||||
|
||||
class SysTray : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SysTray(HyperionDaemon *hyperiond, quint16 webPort);
|
||||
~SysTray();
|
||||
|
||||
|
||||
public slots:
|
||||
void showColorDialog();
|
||||
void setColor(const QColor & color);
|
||||
void closeEvent(QCloseEvent *event);
|
||||
void settings();
|
||||
void setEffect();
|
||||
void clearEfxColor();
|
||||
|
||||
private slots:
|
||||
void iconActivated(QSystemTrayIcon::ActivationReason reason);
|
||||
|
||||
private:
|
||||
void createTrayIcon();
|
||||
|
||||
QAction *quitAction;
|
||||
QAction *startAction;
|
||||
QAction *stopAction;
|
||||
QAction *colorAction;
|
||||
QAction *settingsAction;
|
||||
QAction *clearAction;
|
||||
|
||||
QSystemTrayIcon *_trayIcon;
|
||||
QMenu *_trayIconMenu;
|
||||
QMenu *_trayIconEfxMenu;
|
||||
QColorDialog _colorDlg;
|
||||
HyperionDaemon *_hyperiond;
|
||||
quint16 _webPort;
|
||||
Hyperion *_hyperion;
|
||||
};
|
Loading…
Reference in New Issue
Block a user