Skip to content

Commit 480db0e

Browse files
feat: unify backends with QT
1 parent 83c1f19 commit 480db0e

13 files changed

Lines changed: 278 additions & 856 deletions

File tree

.github/workflows/ci.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
include:
5353
- os: macos-latest
5454
shell: "bash"
55-
qt_version: ''
55+
qt_version: '6'
5656
- os: ubuntu-latest
5757
shell: "bash"
5858
qt_version: '5'
@@ -61,7 +61,7 @@ jobs:
6161
qt_version: '6'
6262
- os: windows-latest
6363
shell: "msys2 {0}"
64-
qt_version: ''
64+
qt_version: '6'
6565
steps:
6666
- name: Checkout
6767
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
@@ -118,6 +118,8 @@ jobs:
118118
"imagemagick"
119119
"ninja"
120120
"node"
121+
"qtbase"
122+
"qtsvg"
121123
)
122124
brew install "${dependencies[@]}"
123125
@@ -171,6 +173,8 @@ jobs:
171173
mingw-w64-ucrt-x86_64-ninja
172174
mingw-w64-ucrt-x86_64-nodejs
173175
mingw-w64-ucrt-x86_64-toolchain
176+
mingw-w64-ucrt-x86_64-qt6-base
177+
mingw-w64-ucrt-x86_64-qt6-svg
174178
175179
- name: Setup python
176180
id: setup-python

CMakeLists.txt

Lines changed: 47 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -67,32 +67,30 @@ function(tray_copy_default_icons target_name)
6767
endforeach()
6868
endfunction()
6969

70-
if(WIN32)
71-
list(APPEND TRAY_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/tray_windows.c")
70+
find_package(Qt6 COMPONENTS Widgets Svg)
71+
if(Qt6_FOUND)
72+
set(TRAY_QT_VERSION 6)
7273
else()
73-
if(UNIX)
74-
if(APPLE)
75-
find_library(COCOA Cocoa REQUIRED)
76-
list(APPEND TRAY_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/tray_darwin.m")
77-
else()
78-
find_package(LibNotify REQUIRED)
79-
find_package(Qt6 COMPONENTS Widgets Svg)
80-
if(Qt6_FOUND)
81-
set(TRAY_QT_VERSION 6)
82-
else()
83-
find_package(Qt5 REQUIRED COMPONENTS Widgets Svg)
84-
set(TRAY_QT_VERSION 5)
85-
endif()
86-
set(TRAY_QT_VERSION # cmake-lint: disable=C0103
87-
"${TRAY_QT_VERSION}"
88-
CACHE INTERNAL "Qt major version selected by tray"
89-
)
90-
set(CMAKE_AUTOMOC ON)
91-
list(APPEND TRAY_SOURCES
92-
"${CMAKE_CURRENT_SOURCE_DIR}/src/tray_linux.cpp"
93-
"${CMAKE_CURRENT_SOURCE_DIR}/src/QtTrayMenu.cpp"
94-
)
95-
endif()
74+
find_package(Qt5 REQUIRED COMPONENTS Widgets Svg)
75+
set(TRAY_QT_VERSION 5)
76+
endif()
77+
set(TRAY_QT_VERSION # cmake-lint: disable=C0103
78+
"${TRAY_QT_VERSION}"
79+
CACHE INTERNAL "Qt major version selected by tray"
80+
)
81+
set(CMAKE_AUTOMOC ON)
82+
list(APPEND TRAY_SOURCES
83+
"${CMAKE_CURRENT_SOURCE_DIR}/src/tray_qt.cpp"
84+
"${CMAKE_CURRENT_SOURCE_DIR}/src/QtTrayMenu.cpp"
85+
)
86+
87+
if(UNIX AND NOT APPLE)
88+
find_package(LibNotify)
89+
if(LIBNOTIFY_FOUND)
90+
list(APPEND TRAY_COMPILE_DEFINITIONS TRAY_USE_LIBNOTIFY=1)
91+
list(APPEND TRAY_EXTERNAL_LIBRARIES ${LIBNOTIFY_LIBRARIES})
92+
list(APPEND TRAY_EXTERNAL_DIRECTORIES ${LIBNOTIFY_LIBRARY_DIRS})
93+
list(APPEND TRAY_EXTERNAL_INCLUDES ${LIBNOTIFY_INCLUDE_DIRS})
9694
endif()
9795
endif()
9896

@@ -104,27 +102,32 @@ target_include_directories(${PROJECT_NAME}
104102
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
105103
$<INSTALL_INTERFACE:include>)
106104

107-
if(WIN32)
108-
if(MSVC)
109-
list(APPEND TRAY_COMPILE_OPTIONS "/MT$<$<CONFIG:Debug>:d>")
110-
endif()
105+
if(WIN32 AND MSVC)
106+
list(APPEND TRAY_COMPILE_OPTIONS "/MT$<$<CONFIG:Debug>:d>")
107+
endif()
108+
109+
if(TRAY_QT_VERSION EQUAL 6)
110+
list(APPEND TRAY_EXTERNAL_LIBRARIES Qt6::Widgets Qt6::Svg)
111111
else()
112-
if(UNIX)
113-
if(APPLE)
114-
list(APPEND TRAY_EXTERNAL_LIBRARIES ${COCOA})
115-
else()
116-
if(TRAY_QT_VERSION EQUAL 6)
117-
list(APPEND TRAY_EXTERNAL_LIBRARIES Qt6::Widgets Qt6::Svg)
118-
else()
119-
list(APPEND TRAY_EXTERNAL_LIBRARIES Qt5::Widgets Qt5::Svg)
120-
endif()
121-
list(APPEND TRAY_LIBNOTIFY=1)
122-
list(APPEND TRAY_EXTERNAL_LIBRARIES ${LIBNOTIFY_LIBRARIES})
123-
124-
include_directories(SYSTEM ${LIBNOTIFY_INCLUDE_DIRS})
125-
link_directories(${LIBNOTIFY_LIBRARY_DIRS})
112+
list(APPEND TRAY_EXTERNAL_LIBRARIES Qt5::Widgets Qt5::Svg)
113+
endif()
114+
115+
if(TRAY_EXTERNAL_INCLUDES)
116+
target_include_directories(${PROJECT_NAME}
117+
SYSTEM PRIVATE
118+
${TRAY_EXTERNAL_INCLUDES})
119+
endif()
120+
121+
if(TRAY_COMPILE_DEFINITIONS)
122+
target_compile_definitions(${PROJECT_NAME} PRIVATE ${TRAY_COMPILE_DEFINITIONS})
123+
endif()
124+
125+
if(TRAY_EXTERNAL_DIRECTORIES)
126+
foreach(TRAY_EXTERNAL_DIRECTORY IN LISTS TRAY_EXTERNAL_DIRECTORIES)
127+
if(TRAY_EXTERNAL_DIRECTORY)
128+
target_link_directories(${PROJECT_NAME} PRIVATE "${TRAY_EXTERNAL_DIRECTORY}")
126129
endif()
127-
endif()
130+
endforeach()
128131
endif()
129132

130133
add_library(tray::tray ALIAS ${PROJECT_NAME})
@@ -142,7 +145,6 @@ if(TRAY_IS_TOP_LEVEL)
142145
endif()
143146

144147
target_compile_options(${PROJECT_NAME} PRIVATE ${TRAY_COMPILE_OPTIONS})
145-
target_link_directories(${PROJECT_NAME} PRIVATE ${TRAY_EXTERNAL_DIRECTORIES})
146148
target_link_libraries(${PROJECT_NAME} PRIVATE ${TRAY_EXTERNAL_LIBRARIES})
147149

148150
#

README.md

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
## About
88

9-
Cross-platform, super tiny C99 implementation of a system tray icon with a popup menu and notifications.
9+
Cross-platform C++17 Qt-backed system tray icon with a popup menu and notifications.
1010

11-
The code is C++ friendly and will compile fine in C++98 and up. This is a fork of
11+
This is a fork of
1212
[dmikushin/tray](https://github.com/dmikushin/tray) and is intended to add additional features required for our own
1313
[Sunshine](https://github.com/LizardByte/Sunshine) project.
1414

@@ -32,18 +32,20 @@ This fork adds the following features:
3232

3333
## Supported platforms
3434

35-
* Linux/Qt (Qt5 or Qt6 Widgets)
36-
* Windows XP or newer (shellapi.h)
37-
* MacOS (Cocoa/AppKit)
35+
* Linux with Qt5 or Qt6 Widgets
36+
* macOS with Qt5 or Qt6 Widgets
37+
* Windows with Qt5 or Qt6 Widgets
3838

3939
## Prerequisites
4040

4141
* CMake
4242
* [Ninja](https://ninja-build.org/), to have the same build commands on all platforms.
43+
* C++17 compiler
44+
* Qt5 or Qt6 Widgets and Svg modules
4345

44-
### Linux Dependencies
46+
### Platform Dependencies
4547

46-
Install either Qt6 _or_ Qt5 as well as libnotify development packages. The Linux backend requires libnotify and Qt Widgets+Svg modules.
48+
Install either Qt6 _or_ Qt5. Linux builds can also use libnotify when the development package is available.
4749

4850
<div class="tabbed">
4951

@@ -74,6 +76,20 @@ Install either Qt6 _or_ Qt5 as well as libnotify development packages. The Linux
7476
sudo dnf install qt5-qtbase-devel qt5-qtsvg-devel libnotify-devel
7577
```
7678

79+
- <b class="tab-title">macOS</b>
80+
```bash
81+
brew install cmake ninja qtbase qtsvg
82+
```
83+
84+
- <b class="tab-title">Windows (MSYS2 UCRT64)</b>
85+
```bash
86+
pacman -S mingw-w64-ucrt-x86_64-cmake \
87+
mingw-w64-ucrt-x86_64-ninja \
88+
mingw-w64-ucrt-x86_64-toolchain \
89+
mingw-w64-ucrt-x86_64-qt6-base \
90+
mingw-w64-ucrt-x86_64-qt6-svg
91+
```
92+
7793
</div>
7894

7995
## Building

docs/Doxyfile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,13 @@
2424
# project metadata
2525
DOCSET_BUNDLE_ID = dev.lizardbyte.tray
2626
DOCSET_PUBLISHER_ID = dev.lizardbyte.tray.documentation
27-
PROJECT_BRIEF = "Cross-platform, super tiny C99 implementation of a system tray icon with a popup menu and notifications."
27+
PROJECT_BRIEF = "Cross-platform C++17 Qt-backed system tray icon with a popup menu and notifications."
2828
PROJECT_NAME = tray
2929

3030
# project specific settings
3131
DOT_GRAPH_MAX_NODES = 50
3232
IMAGE_PATH = ../docs/images
3333
INCLUDE_PATH =
34-
PREDEFINED += TRAY_WINAPI
3534

3635
# files and directories to process
3736
USE_MDFILE_AS_MAINPAGE = ../README.md

src/QtTrayMenu.cpp

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -250,33 +250,35 @@ struct tray_menu *QtTrayMenu::getTrayMenuItem(QAction *action) { // NOSONAR(cpp
250250
}
251251

252252
void QtTrayMenu::onMessageClicked() const {
253-
if (notificationCallback != nullptr) {
254-
notificationCallback();
253+
if (notificationCallback == nullptr) {
254+
return;
255255
}
256+
257+
auto callback = std::move(notificationCallback);
258+
notificationCallback = nullptr;
259+
callback();
256260
}
257261

258262
void QtTrayMenu::configureAppMetadata(const QString &appName, const QString &appDisplayName, const QString &desktopName) const {
259263
const QString effective_name = !appName.isEmpty() ? appName : QStringLiteral("tray");
260-
if (QApplication::applicationName().isEmpty()) {
264+
if (!appName.isEmpty() || QApplication::applicationName().isEmpty() || QApplication::applicationName() == QStringLiteral("TrayMenuApp")) {
261265
QApplication::setApplicationName(effective_name);
262266
}
263267

264-
if (QApplication::applicationDisplayName().isEmpty()) {
265-
if (!appDisplayName.isEmpty()) {
266-
QApplication::setApplicationDisplayName(appDisplayName);
267-
} else {
268-
const QString display_name =
269-
(trayStruct && trayStruct->tooltip) ? QString::fromUtf8(trayStruct->tooltip) : effective_name;
270-
QApplication::setApplicationDisplayName(display_name);
271-
}
268+
if (!appDisplayName.isEmpty()) {
269+
QApplication::setApplicationDisplayName(appDisplayName);
270+
} else if (QApplication::applicationDisplayName().isEmpty()) {
271+
const QString display_name =
272+
(trayStruct && trayStruct->tooltip) ? QString::fromUtf8(trayStruct->tooltip) : effective_name;
273+
QApplication::setApplicationDisplayName(display_name);
272274
}
273275

274-
if (!QApplication::desktopFileName().isEmpty()) {
276+
if (!desktopName.isEmpty()) {
277+
QApplication::setDesktopFileName(desktopName);
275278
return;
276279
}
277280

278-
if (!desktopName.isEmpty()) {
279-
QApplication::setDesktopFileName(desktopName);
281+
if (!QApplication::desktopFileName().isEmpty()) {
280282
return;
281283
}
282284

@@ -347,3 +349,7 @@ void QtTrayMenu::clickMessage() const {
347349
}
348350
emit trayIcon->messageClicked();
349351
}
352+
353+
void QtTrayMenu::clearMessageCallback() const {
354+
notificationCallback = nullptr;
355+
}

src/QtTrayMenu.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ class QtTrayMenu: public QObject {
101101
*/
102102
void clickMessage() const;
103103

104+
/**
105+
* @brief Clear the stored popup message callback
106+
*/
107+
void clearMessageCallback() const;
108+
104109
/**
105110
* @brief Check if QtTrayMenu supports messages
106111
* @return true if messages can be shown
@@ -137,7 +142,7 @@ class QtTrayMenu: public QObject {
137142
bool running = false;
138143
bool blockingEventLoop = false;
139144
struct tray_menu *getTrayMenuItem(QAction *action);
140-
std::function<void()> notificationCallback = nullptr;
145+
mutable std::function<void()> notificationCallback = nullptr;
141146

142147
private slots:
143148
void onExitRequested();

src/example.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,8 @@
99
// local includes
1010
#include "tray.h"
1111

12-
#if defined(_WIN32)
13-
#define TRAY_ICON1 "icon.ico" ///< Path to first icon.
14-
#define TRAY_ICON2 "icon.ico" ///< Path to second icon.
15-
#else
16-
#define TRAY_ICON1 "icon.png"
17-
#define TRAY_ICON2 "icon.png"
18-
#endif
12+
#define TRAY_ICON1 "icon.png" ///< Path to first icon.
13+
#define TRAY_ICON2 "icon.png" ///< Path to second icon.
1914

2015
static struct tray tray;
2116

src/tray.h

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
#ifndef TRAY_H
66
#define TRAY_H
77

8-
#if defined(_WIN32)
9-
#include <Windows.h>
10-
#endif
11-
128
#ifdef __cplusplus
139
extern "C" {
1410
#endif
@@ -77,17 +73,15 @@ extern "C" {
7773
/**
7874
* @brief Simulate a notification click, invoking the notification callback (for testing purposes).
7975
*
80-
* On Linux (Qt): triggers the stored notification callback as if the user clicked the notification.
81-
* On other platforms: no-op.
76+
* Triggers the stored notification callback as if the user clicked the notification.
8277
*/
8378
void tray_simulate_notification_click(void);
8479

8580
/**
8681
* @brief Simulate clicking a top-level menu item by index (for testing purposes).
8782
*
88-
* On Linux (Qt): triggers the QAction associated with the given top-level menu
89-
* index (separators and submenus are ignored).
90-
* On other platforms: no-op.
83+
* Triggers the QAction associated with the given top-level menu index
84+
* (separators and submenus are ignored).
9185
*
9286
* @param index Zero-based index in the top-level tray menu.
9387
*/
@@ -101,9 +95,8 @@ extern "C" {
10195
/**
10296
* @brief Set a callback for log messages produced by the tray library.
10397
*
104-
* On Linux the callback is installed as a Qt message handler so all Qt
105-
* diagnostic output is routed through it. On other platforms this function
106-
* is a no-op.
98+
* The callback is installed as a Qt message handler so all Qt diagnostic
99+
* output is routed through it.
107100
*
108101
* @param cb Callback invoked with level (0=debug, 1=info, 2=warning, 3=error)
109102
* and the message string. Pass NULL to restore the default logging behaviour.
@@ -113,9 +106,8 @@ extern "C" {
113106
/**
114107
* @brief Set application metadata used by the tray library.
115108
*
116-
* Must be called before tray_init(). On Linux (Qt), sets the Qt application
117-
* name, display name, and desktop file name used for D-Bus registration. On
118-
* other platforms this function is a no-op.
109+
* Must be called before tray_init(). Sets the Qt application name, display
110+
* name, and desktop file name.
119111
*
120112
* @param app_name Application name used as a technical identifier (e.g., for
121113
* D-Bus registration). Converted to lowercase automatically. NULL uses the
@@ -127,14 +119,6 @@ extern "C" {
127119
*/
128120
void tray_set_app_info(const char *app_name, const char *app_display_name, const char *desktop_name);
129121

130-
#if defined(_WIN32)
131-
/**
132-
* @brief Get the tray window handle.
133-
* @return The window handle.
134-
*/
135-
HWND tray_get_hwnd(void);
136-
#endif
137-
138122
#ifdef __cplusplus
139123
} // extern "C"
140124
#endif

0 commit comments

Comments
 (0)