diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index 615b531fa..67968c1b4 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -181,7 +181,7 @@ jobs: if: ( matrix.QT_VERSION == 5 ) shell: bash run: | - export Qt5_DIR=`brew --prefix qt5`; + export Qt5_DIR=`brew --prefix qt@5`; echo "Qt5_DIR=$Qt5_DIR" >> $GITHUB_ENV # Build process diff --git a/cmake/packages.cmake b/cmake/packages.cmake index 57eb0c551..c8192db20 100644 --- a/cmake/packages.cmake +++ b/cmake/packages.cmake @@ -117,11 +117,12 @@ endif() if ( ENABLE_CEC ) string(CONCAT CPACK_DEBIAN_PACKAGE_RECOMMENDS "${CPACK_DEBIAN_PACKAGE_RECOMMENDS}" ", libp8-platform-dev" ) endif() + +SET ( CPACK_DEBIAN_PACKAGE_SUGGESTS "libx11-6" ) if ( ENABLE_SYSTRAY ) - string(CONCAT CPACK_DEBIAN_PACKAGE_RECOMMENDS "${CPACK_DEBIAN_PACKAGE_RECOMMENDS}" ", libgtk-3-0" ) + string(CONCAT CPACK_DEBIAN_PACKAGE_SUGGESTS "${CPACK_DEBIAN_PACKAGE_SUGGESTS}" ", libgtk-3-0" ) endif() -SET ( CPACK_DEBIAN_PACKAGE_SUGGESTS "libx11-6" ) SET ( CPACK_DEBIAN_PACKAGE_SECTION "Miscellaneous" ) # .rpm for rpm diff --git a/resources/icons/hyperhdr-tray-icon.svg b/resources/icons/hyperhdr-tray-icon.svg new file mode 100644 index 000000000..611ce163f --- /dev/null +++ b/resources/icons/hyperhdr-tray-icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/sources/grabber/AVF/AVFGrabber.mm b/sources/grabber/AVF/AVFGrabber.mm index 9091f8559..d1e126327 100644 --- a/sources/grabber/AVF/AVFGrabber.mm +++ b/sources/grabber/AVF/AVFGrabber.mm @@ -153,7 +153,7 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB if (_isAVF) { if (!_permission) - QTimer::singleShot(5000, this, &AVFGrabber::getPermission); + QMetaObject::invokeMethod(this, [this]{ QTimer::singleShot(5000, this, &AVFGrabber::getPermission); }); else { Info(_log, "Got the video permission. Now trying to start HyperHDR's video grabber."); diff --git a/sources/hyperhdr/SystrayHandler.cpp b/sources/hyperhdr/SystrayHandler.cpp index 8318c2d0b..5ed99c8de 100644 --- a/sources/hyperhdr/SystrayHandler.cpp +++ b/sources/hyperhdr/SystrayHandler.cpp @@ -35,6 +35,10 @@ #include #endif +#ifdef __APPLE__ + #include +#endif + #include #include #include @@ -147,41 +151,22 @@ void SystrayHandler::close() } } -static void loadPng(std::unique_ptr& menu, QString filename, QString rootFolder) +static void loadSvg(std::unique_ptr& menu, QString filename, QString rootFolder , QString destFilename = "") { -#ifdef __linux__ - QString fullPath = rootFolder + "/icons/" + QFileInfo(filename).fileName(); - QFileInfo iconFile(fullPath); - QDir dir(iconFile.absolutePath()); - if (!dir.exists()) - dir.mkpath("."); - - if (!iconFile.exists()) - { - QFile stream(filename); - stream.open(QIODevice::ReadOnly); - QByteArray ar = stream.readAll(); - stream.close(); - - QFile newIcon(fullPath); - newIcon.open(QIODevice::WriteOnly); - newIcon.write(ar); - newIcon.close(); - } - menu->label = fullPath.toStdString(); -#else - QFile stream(filename); - stream.open(QIODevice::ReadOnly); - QByteArray ar = stream.readAll(); - stream.close(); - menu->icon.resize(ar.size()); - memcpy(menu->icon.data(), ar.data(), ar.size()); +#ifdef __linux__ + int iconDim = 16; + #else + #ifdef __APPLE__ + int iconDim = 18; + #else + int iconDim = 18; + + if (filename == ":/hyperhdr-tray-icon.svg") + iconDim = 36; + #endif #endif -} -static void loadSvg(std::unique_ptr& menu, QString filename, QString rootFolder , QString destFilename = "") -{ #ifdef __linux__ if (destFilename.isEmpty()) { @@ -199,7 +184,7 @@ static void loadSvg(std::unique_ptr& menu, QString filename, QStrin QByteArray ar; QBuffer buffer(&ar); buffer.open(QIODevice::WriteOnly); - HyperImage::svg2png(filename, 16, 16, buffer); + HyperImage::svg2png(filename, iconDim, iconDim, buffer); QFile newIcon(fullPath); newIcon.open(QIODevice::WriteOnly); @@ -212,7 +197,7 @@ static void loadSvg(std::unique_ptr& menu, QString filename, QStrin QByteArray ar; QBuffer buffer(&ar); buffer.open(QIODevice::WriteOnly); - HyperImage::svg2png(filename, 18, 18, buffer); + HyperImage::svg2png(filename, iconDim, iconDim, buffer); menu->icon.resize(ar.size()); memcpy(menu->icon.data(), ar.data(), ar.size()); @@ -226,8 +211,7 @@ void SystrayHandler::createSystray() std::unique_ptr mainMenu = std::unique_ptr(new SystrayMenu); - // main icon - loadPng(mainMenu, ":/hyperhdr-icon-32px.png", _rootFolder); + loadSvg(mainMenu, ":/hyperhdr-tray-icon.svg", _rootFolder); // settings menu std::unique_ptr settingsMenu = std::unique_ptr(new SystrayMenu); @@ -467,7 +451,7 @@ void SystrayHandler::setColor(ColorRgb color) { std::shared_ptr instanceManager = _instanceManager.lock(); if (instanceManager) - instanceManager->setInstanceColor(_selectedInstance, 1, color, 0); + QUEUE_CALL_4(instanceManager.get(), setInstanceColor, int, _selectedInstance, int, 1, ColorRgb, color, int, 0); } @@ -508,6 +492,19 @@ void SystrayHandler::settings() printf("xdg-open failed. xdg-utils package is required.\n"); } #endif + +#ifdef __APPLE__ + std::string slink = link.toStdString(); + CFURLRef url = CFURLCreateWithBytes( + NULL, + (UInt8*)slink.c_str(), + slink.length(), + kCFStringEncodingASCII, + NULL + ); + LSOpenCFURLRef(url, 0); + CFRelease(url); +#endif #ifndef _WIN32 // restoring stdout @@ -521,14 +518,14 @@ void SystrayHandler::setEffect(QString effect) { std::shared_ptr instanceManager = _instanceManager.lock(); if (instanceManager) - instanceManager->setInstanceEffect(_selectedInstance, effect, 1); + QUEUE_CALL_3(instanceManager.get(), setInstanceEffect, int, _selectedInstance, QString, effect, int, 1); } void SystrayHandler::clearEfxColor() { std::shared_ptr instanceManager = _instanceManager.lock(); if (instanceManager) - instanceManager->clearInstancePriority(_selectedInstance, 1); + QUEUE_CALL_2(instanceManager.get(), clearInstancePriority, int, _selectedInstance, int, 1); } void SystrayHandler::signalInstanceStateChangedHandler(InstanceState state, quint8 instance, const QString& name) diff --git a/sources/hyperhdr/main.cpp b/sources/hyperhdr/main.cpp index 308ea4810..1261f5092 100644 --- a/sources/hyperhdr/main.cpp +++ b/sources/hyperhdr/main.cpp @@ -330,7 +330,7 @@ int main(int argc, char** argv) } catch (std::exception& e) { - Error(log, "HyperHdr Daemon aborted: %s", e.what()); + Error(log, "Main HyperHDR service aborted: %s", e.what()); throw; } @@ -340,10 +340,12 @@ int main(int argc, char** argv) if (systray != nullptr && systray->isInitialized()) { QTimer* timer = new QTimer(systray); - timer->setInterval(300); QObject::connect(timer, &QTimer::timeout, systray, [&systray]() { - systray->loop(); - }); + systray->loop(); + }); + + timer->setInterval(200); + timer->start(); rc = app->exec(); diff --git a/sources/systray/SystrayMacOS.mm b/sources/systray/SystrayMacOS.mm index 62e5ca3ca..05140a249 100644 --- a/sources/systray/SystrayMacOS.mm +++ b/sources/systray/SystrayMacOS.mm @@ -25,12 +25,130 @@ * SOFTWARE. */ +#include #include +#include -bool SystrayInitialize(SystrayMenu* tray){return false;} +@interface MenuDelegate: NSObject +- (void)clicked:(NSMenuItem*)sender; +@end +@implementation MenuDelegate{} + - (void)clicked:(NSMenuItem*)sender + { + id representedObject = sender.representedObject; + struct SystrayMenu* pTrayMenu = (struct SystrayMenu*)[representedObject pointerValue]; + if (pTrayMenu != NULL && pTrayMenu->callback != NULL) + { + pTrayMenu->callback(pTrayMenu); + } + } +@end -int SystrayLoop() { return 0; } +namespace +{ + MenuDelegate* menuDelegate; + NSApplication* app; + NSStatusBar* statusBar; + NSStatusItem* statusItem; +} -void SystrayUpdate(SystrayMenu* tray){} +static NSMenu* nativeMenu(struct SystrayMenu* m) { + NSMenu* menu = [[NSMenu alloc] init]; + [menu setAutoenablesItems:FALSE]; -void SystrayClose(){} + for (; m != NULL && !m->label.empty(); m = m->next.get()) + { + std::string labelText = " " + m->label; + labelText.erase(std::remove(labelText.begin(), labelText.end(), '&'), labelText.end()); + + if (m->label == "-") + { + [menu addItem:[NSMenuItem separatorItem]]; + } + else + { + NSString* labelNS = [NSString stringWithUTF8String: labelText.c_str()]; + NSMenuItem* menuItem = [[NSMenuItem alloc] init]; + [menuItem setTitle:labelNS]; + [menuItem setEnabled:m->isDisabled == 0 ? TRUE : FALSE]; + [menuItem setState:m->isChecked == 1 ? TRUE : FALSE]; + [menuItem setRepresentedObject:[NSValue valueWithPointer:m]]; + + if (m->icon.size()) + { + auto* data = [NSData dataWithBytes: m->icon.data() + length: m->icon.size()]; + + NSImage* image = [[NSImage alloc] initWithData:data]; + [menuItem setImage: image ]; + } + + if (m->callback != NULL) + { + [menuItem setTarget:menuDelegate]; + [menuItem setAction:@selector(clicked:)]; + } + [menu addItem:menuItem]; + if (m->submenu != NULL) + { + [menu setSubmenu:nativeMenu(m->submenu.get()) forItem:menuItem]; + } + } + } + return menu; +} + +bool SystrayInitialize(SystrayMenu* tray) +{ + menuDelegate = [[MenuDelegate alloc] init]; + app = [NSApplication sharedApplication]; + statusBar = [NSStatusBar systemStatusBar]; + statusItem = [statusBar statusItemWithLength:NSVariableStatusItemLength]; + + if (tray != nullptr) + { + SystrayUpdate(tray); + } + + return true; +} + +int SystrayLoop() +{ + NSDate* until = [NSDate distantPast]; + NSEvent* event = [app nextEventMatchingMask:ULONG_MAX untilDate:until + inMode:[NSString stringWithUTF8String:"kCFRunLoopDefaultMode"] dequeue:TRUE]; + if (event) + { + [app sendEvent:event]; + } + return 0; +} + +void SystrayUpdate(SystrayMenu* tray) +{ + double iconHeight = [[NSStatusBar systemStatusBar] thickness]; + + auto* data = [NSData dataWithBytes:tray->icon.data() + length:tray->icon.size()]; + + NSImage* image = [[NSImage alloc] initWithData:data]; + + if (iconHeight < image.size.height) + { + std::cout << "Resizing the main icon: " << image.size.width << "x" << image.size.height << " to height=" << iconHeight << "\n"; + double width = image.size.width * (iconHeight / image.size.height); + [image setSize:NSMakeSize(width, iconHeight)]; + } + + statusItem.button.image = image; + if (!tray->tooltip.empty()) + { + statusItem.button.toolTip = [NSString stringWithUTF8String:tray->tooltip.c_str()]; + } + [statusItem setMenu:nativeMenu(tray->submenu.get())]; +} + +void SystrayClose() +{ +}