Skip to content

Commit

Permalink
Add macOS Systray port
Browse files Browse the repository at this point in the history
  • Loading branch information
awawa-dev committed May 16, 2024
1 parent 6122ff8 commit 92477c9
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/push-master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions cmake/packages.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions resources/icons/hyperhdr-tray-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion sources/grabber/AVF/AVFGrabber.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down
73 changes: 35 additions & 38 deletions sources/hyperhdr/SystrayHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
#include <unistd.h>
#endif

#ifdef __APPLE__
#include <ApplicationServices/ApplicationServices.h>
#endif

#include <QBuffer>
#include <QFile>
#include <QCoreApplication>
Expand Down Expand Up @@ -147,41 +151,22 @@ void SystrayHandler::close()
}
}

static void loadPng(std::unique_ptr<SystrayMenu>& menu, QString filename, QString rootFolder)
static void loadSvg(std::unique_ptr<SystrayMenu>& 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<SystrayMenu>& menu, QString filename, QString rootFolder , QString destFilename = "")
{
#ifdef __linux__
if (destFilename.isEmpty())
{
Expand All @@ -199,7 +184,7 @@ static void loadSvg(std::unique_ptr<SystrayMenu>& 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);
Expand All @@ -212,7 +197,7 @@ static void loadSvg(std::unique_ptr<SystrayMenu>& 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());
Expand All @@ -226,8 +211,7 @@ void SystrayHandler::createSystray()

std::unique_ptr<SystrayMenu> mainMenu = std::unique_ptr<SystrayMenu>(new SystrayMenu);

// main icon
loadPng(mainMenu, ":/hyperhdr-icon-32px.png", _rootFolder);
loadSvg(mainMenu, ":/hyperhdr-tray-icon.svg", _rootFolder);

// settings menu
std::unique_ptr<SystrayMenu> settingsMenu = std::unique_ptr<SystrayMenu>(new SystrayMenu);
Expand Down Expand Up @@ -467,7 +451,7 @@ void SystrayHandler::setColor(ColorRgb color)
{
std::shared_ptr<HyperHdrManager> 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);
}


Expand Down Expand Up @@ -508,6 +492,19 @@ void SystrayHandler::settings()
printf("xdg-open <http_link> 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
Expand All @@ -521,14 +518,14 @@ void SystrayHandler::setEffect(QString effect)
{
std::shared_ptr<HyperHdrManager> 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<HyperHdrManager> 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)
Expand Down
10 changes: 6 additions & 4 deletions sources/hyperhdr/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

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

Expand Down
126 changes: 122 additions & 4 deletions sources/systray/SystrayMacOS.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,130 @@
* SOFTWARE.
*/

#include <Cocoa/Cocoa.h>
#include <systray/Systray.h>
#include <iostream>

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()
{
}

0 comments on commit 92477c9

Please sign in to comment.