Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b409a01
fix(linux): migrate to qt tray
ReenigneArcher Mar 24, 2026
1055613
style: sonar fixes
ReenigneArcher Mar 24, 2026
290e3a5
fix(linux): tray destruction
ReenigneArcher Mar 24, 2026
0fccbce
fix(linux): notification icon
ReenigneArcher Mar 24, 2026
e969048
fix(linux): support themed icons
ReenigneArcher Mar 24, 2026
39992ed
Simulate notification clicks and D-Bus handler
ReenigneArcher Mar 25, 2026
7f3e3bd
Add tray log callback and menu positioning fixes
ReenigneArcher Mar 26, 2026
25d0cf4
Improve tray menu positioning and icon handling
ReenigneArcher Mar 26, 2026
e9363c6
Fix tray menu focus and icon loading on Linux
ReenigneArcher Mar 26, 2026
cac14ab
Remove FindAPPINDICATOR and FindLibNotify
ReenigneArcher Mar 26, 2026
edd9008
Add SVG support and improve Linux tray behavior
ReenigneArcher Mar 26, 2026
e79887d
CI: add Qt5/Qt6 matrix and conditional deps
ReenigneArcher Mar 26, 2026
a1f0a56
Use QGuiApplication for applicationDisplayName
ReenigneArcher Mar 26, 2026
21f70b5
Refactor CMake for subproject and icons
ReenigneArcher Mar 26, 2026
4372500
Add tray_set_app_info and Qt thread fixes
ReenigneArcher Mar 27, 2026
214ea00
Fix tray callback issues
ReenigneArcher Mar 27, 2026
144dad5
style: sonar fixes
ReenigneArcher Mar 27, 2026
e8bc13a
test: improve linux test coverage
ReenigneArcher Mar 27, 2026
0e634d5
Wrap installs in TRAY_IS_TOP_LEVEL check
ReenigneArcher Mar 27, 2026
bf002e2
fix(linux): reset notifications during tray tray destruction
ReenigneArcher Mar 27, 2026
3e57b84
Force minimal QPA when no display endpoint
ReenigneArcher Mar 27, 2026
ff76d0e
Autodiscover WAYLAND_DISPLAY from XDG_RUNTIME_DIR
ReenigneArcher Mar 28, 2026
f207e17
Include <stdlib.h> in tray_windows.c
ReenigneArcher Mar 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 41 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}

build:
name: Build (${{ matrix.os }} - ${{ matrix.appindicator || 'default' }})
name: Build (${{ matrix.os }}${{ matrix.qt_version && format(', Qt{0}', matrix.qt_version) || '' }})
defaults:
run:
shell: ${{ matrix.shell }}
Expand All @@ -49,16 +49,16 @@ jobs:
include:
- os: macos-latest
shell: "bash"
qt_version: ''
- os: ubuntu-latest
appindicator: "libayatana-appindicator3-dev"
appindicator_type: "ayatana"
shell: "bash"
qt_version: '5'
- os: ubuntu-latest
appindicator: "libappindicator3-dev"
appindicator_type: "legacy"
shell: "bash"
qt_version: '6'
- os: windows-latest
shell: "msys2 {0}"
qt_version: ''
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand All @@ -67,23 +67,41 @@ jobs:

- name: Setup Dependencies Linux
if: runner.os == 'Linux'
env:
QT_VERSION: ${{ matrix.qt_version }}
run: |
dependencies=(
"adwaita-icon-theme"
"build-essential"
"cmake"
"imagemagick"
"ninja-build"
"xvfb"
)

if [[ "${QT_VERSION}" == "5" ]]; then
dependencies+=(
"libqt5svg5-dev"
"qt5-gtk-platformtheme"
"qtbase5-dev"
)
elif [[ "${QT_VERSION}" == "6" ]]; then
dependencies+=(
"qt6-base-dev"
"qt6-svg-dev"
)
else
echo "Unsupported QT_VERSION: ${QT_VERSION}"
exit 1
fi

sudo apt-get update
sudo apt-get install -y \
build-essential \
cmake \
${{ matrix.appindicator }} \
imagemagick \
libglib2.0-dev \
libnotify-dev \
ninja-build \
xvfb
sudo apt-get install -y "${dependencies[@]}"

- name: Setup virtual desktop
if: runner.os == 'Linux'
uses: LizardByte/actions/actions/virtual_desktop@0affa4f7bcb27562658960eee840eff8ff844578 # v2026.328.161128
with:
appindicator-version: ${{ matrix.appindicator_type }}
environment: mate

- name: Setup Dependencies macOS
Expand Down Expand Up @@ -169,7 +187,7 @@ jobs:
echo "python-path=${python_path}"
echo "python-path=${python_path}" >> "${GITHUB_OUTPUT}"

- name: Build
- name: Configure
run: |
mkdir -p build

Expand All @@ -186,7 +204,9 @@ jobs:
-B build \
-G Ninja \
-S .
ninja -C build

- name: Build
run: ninja -C build

- name: Init tray icon (Windows)
if: runner.os == 'Windows'
Expand Down Expand Up @@ -228,6 +248,8 @@ jobs:
# TODO: tests randomly hang on Linux, https://github.com/LizardByte/tray/issues/45
timeout-minutes: 3
working-directory: build/tests
env:
QT_QPA_PLATFORMTHEME: ${{ runner.os == 'Linux' && matrix.qt_version == '5' && 'gtk3' || '' }}
run: ./test_tray --gtest_color=yes --gtest_output=xml:test_results.xml

- name: Upload screenshots
Expand All @@ -236,7 +258,7 @@ jobs:
(steps.test.outcome == 'success' || steps.test.outcome == 'failure')
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: tray-screenshots-${{ runner.os }}${{ matrix.appindicator && format('-{0}', matrix.appindicator) || '' }}
name: tray-screenshots-${{ runner.os }}${{ matrix.qt_version && format('-qt{0}', matrix.qt_version) || '' }}
path: build/tests/screenshots
if-no-files-found: error

Expand All @@ -263,10 +285,7 @@ jobs:
- name: Set codecov flags
id: codecov_flags
run: |
flags="${{ runner.os }}"
if [ -n "${{ matrix.appindicator }}" ]; then
flags="${flags},${{ matrix.appindicator }}"
fi
flags="${{ runner.os }}${{ matrix.qt_version && format('-qt{0}', matrix.qt_version) || '' }}"
echo "flags=${flags}" >> "${GITHUB_OUTPUT}"

- name: Upload coverage
Expand Down
118 changes: 76 additions & 42 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,99 +5,133 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) # target_link_directories
project(tray VERSION 0.0.0
DESCRIPTION "A cross-platform system tray library"
HOMEPAGE_URL "https://app.lizardbyte.dev"
LANGUAGES C)
LANGUAGES C CXX)

set(PROJECT_LICENSE "MIT")

set(TRAY_IS_TOP_LEVEL OFF)
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(TRAY_IS_TOP_LEVEL ON)
endif()

if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'Release' as none was specified.")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
endif()

# Add our custom CMake modules to the global path
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Add our custom CMake modules without overriding parent project settings.
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

#
# Project optional configuration
#
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
option(BUILD_DOCS "Build documentation" ON)
option(BUILD_TESTS "Build tests" ON)
endif()
option(BUILD_DOCS "Build documentation" ${TRAY_IS_TOP_LEVEL})
option(BUILD_TESTS "Build tests" ${TRAY_IS_TOP_LEVEL})
option(BUILD_EXAMPLE "Build example app" ${TRAY_IS_TOP_LEVEL})

# Generate 'compile_commands.json' for clang_complete
set(CMAKE_COLOR_MAKEFILE ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

find_package (PkgConfig REQUIRED)
find_package(PkgConfig)

file(GLOB TRAY_SOURCES
"${CMAKE_SOURCE_DIR}/src/*.h"
"${CMAKE_SOURCE_DIR}/icons/*.ico"
"${CMAKE_SOURCE_DIR}/icons/*.png")
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.h"
)

set(TRAY_ICON_ICO "${CMAKE_CURRENT_SOURCE_DIR}/icons/icon.ico")
set(TRAY_ICON_PNG "${CMAKE_CURRENT_SOURCE_DIR}/icons/icon.png")
set(TRAY_ICON_SVG "${CMAKE_CURRENT_SOURCE_DIR}/icons/icon.svg")
set(TRAY_ICON_FILES
"${TRAY_ICON_ICO}"
"${TRAY_ICON_PNG}"
"${TRAY_ICON_SVG}"
)

set(_TRAY_ICON_ICO "${TRAY_ICON_ICO}" CACHE INTERNAL "Default tray ICO icon path")
set(_TRAY_ICON_PNG "${TRAY_ICON_PNG}" CACHE INTERNAL "Default tray PNG icon path")
set(_TRAY_ICON_SVG "${TRAY_ICON_SVG}" CACHE INTERNAL "Default tray SVG icon path")

# Copy default tray icon files into the output directory of the specified target.
function(tray_copy_default_icons target_name)
if(NOT TARGET "${target_name}")
message(FATAL_ERROR "tray_copy_default_icons expected an existing target: ${target_name}")
endif()

foreach(icon_file IN LISTS TRAY_ICON_FILES)
add_custom_command(TARGET "${target_name}" POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${icon_file}"
"$<TARGET_FILE_DIR:${target_name}>"
COMMENT "Copying ${icon_file} to $<TARGET_FILE_DIR:${target_name}>")
endforeach()
endfunction()

if(WIN32)
list(APPEND TRAY_SOURCES "${CMAKE_SOURCE_DIR}/src/tray_windows.c")
list(APPEND TRAY_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/tray_windows.c")
else()
if(UNIX)
if(APPLE)
find_library(COCOA Cocoa REQUIRED)
list(APPEND TRAY_SOURCES "${CMAKE_SOURCE_DIR}/src/tray_darwin.m")
list(APPEND TRAY_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/tray_darwin.m")
else()
find_package(APPINDICATOR REQUIRED)
find_package(LibNotify REQUIRED)
list(APPEND TRAY_SOURCES "${CMAKE_SOURCE_DIR}/src/tray_linux.c")
find_package(Qt6 COMPONENTS Widgets DBus Svg)
if(Qt6_FOUND)
set(TRAY_QT_VERSION 6)
else()
find_package(Qt5 REQUIRED COMPONENTS Widgets DBus Svg)
set(TRAY_QT_VERSION 5)
endif()
set(TRAY_QT_VERSION # cmake-lint: disable=C0103
"${TRAY_QT_VERSION}"
CACHE INTERNAL "Qt major version selected by tray"
)
set(CMAKE_AUTOMOC ON)
list(APPEND TRAY_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/tray_linux.cpp")
endif()
endif()
endif()

add_library(${PROJECT_NAME} STATIC ${TRAY_SOURCES})
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:include>)

if(WIN32)
list(APPEND TRAY_DEFINITIONS TRAY_WINAPI=1 WIN32_LEAN_AND_MEAN NOMINMAX)
if(MSVC)
list(APPEND TRAY_COMPILE_OPTIONS "/MT$<$<CONFIG:Debug>:d>")
endif()
else()
if(UNIX)
if(APPLE)
list(APPEND TRAY_DEFINITIONS TRAY_APPKIT=1)
list(APPEND TRAY_EXTERNAL_LIBRARIES ${COCOA})
else()
list(APPEND TRAY_COMPILE_OPTIONS ${APPINDICATOR_CFLAGS})
list(APPEND TRAY_EXTERNAL_DIRECTORIES ${APPINDICATOR_LIBRARY_DIRS})
list(APPEND TRAY_DEFINITIONS TRAY_APPINDICATOR=1)
if(APPINDICATOR_AYATANA)
list(APPEND TRAY_DEFINITIONS TRAY_AYATANA_APPINDICATOR=1)
if(TRAY_QT_VERSION EQUAL 6)
list(APPEND TRAY_EXTERNAL_LIBRARIES Qt6::Widgets Qt6::DBus Qt6::Svg)
else()
list(APPEND TRAY_EXTERNAL_LIBRARIES Qt5::Widgets Qt5::DBus Qt5::Svg)
endif()
if(APPINDICATOR_LEGACY)
list(APPEND TRAY_DEFINITIONS TRAY_LEGACY_APPINDICATOR=1)
endif()
list(APPEND TRAY_LIBNOTIFY=1)
list(APPEND TRAY_EXTERNAL_LIBRARIES ${APPINDICATOR_LIBRARIES} ${LIBNOTIFY_LIBRARIES})

include_directories(SYSTEM ${APPINDICATOR_INCLUDE_DIRS} ${LIBNOTIFY_INCLUDE_DIRS})
link_directories(${APPINDICATOR_LIBRARY_DIRS} ${LIBNOTIFY_LIBRARY_DIRS})
endif()
endif()
endif()

add_library(tray::tray ALIAS ${PROJECT_NAME})

add_executable(tray_example "${CMAKE_SOURCE_DIR}/src/example.c")
target_link_libraries(tray_example tray::tray)

configure_file("${CMAKE_SOURCE_DIR}/icons/icon.ico" "${CMAKE_BINARY_DIR}/icon.ico" COPYONLY)
configure_file("${CMAKE_SOURCE_DIR}/icons/icon.png" "${CMAKE_BINARY_DIR}/icon.png" COPYONLY)

INSTALL(TARGETS tray tray DESTINATION lib)
if(BUILD_EXAMPLE)
add_executable(tray_example "${CMAKE_CURRENT_SOURCE_DIR}/src/example.c")
target_link_libraries(tray_example tray::tray)
tray_copy_default_icons(tray_example)
endif()

IF(NOT WIN32)
INSTALL(FILES tray.h DESTINATION include)
ENDIF()
if(TRAY_IS_TOP_LEVEL)
install(TARGETS tray DESTINATION lib)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/tray.h" DESTINATION include)
install(FILES ${TRAY_ICON_FILES} DESTINATION share/tray/icons)
endif()

target_compile_definitions(${PROJECT_NAME} PRIVATE ${TRAY_DEFINITIONS})
target_compile_options(${PROJECT_NAME} PRIVATE ${TRAY_COMPILE_OPTIONS})
target_link_directories(${PROJECT_NAME} PRIVATE ${TRAY_EXTERNAL_DIRECTORIES})
target_link_libraries(${PROJECT_NAME} PRIVATE ${TRAY_EXTERNAL_LIBRARIES})
Expand Down
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ The code is C++ friendly and will compile fine in C++98 and up. This is a fork o
This fork adds the following features:

- system tray notifications
- support for both linux appindicator versions
- unit tests
- code coverage
- refactored code, e.g. moved source code into the `src` directory
- doxygen documentation, and readthedocs configuration
- refactored code, e.g., moved source code into the `src` directory
- doxygen documentation and readthedocs configuration

## Screenshots

Expand All @@ -33,32 +32,47 @@ This fork adds the following features:

## Supported platforms

* Linux/Gtk (libayatana-appindicator3 or libappindicator3)
* Linux/Qt (Qt5 or Qt6 Widgets)
* Windows XP or newer (shellapi.h)
* MacOS (Cocoa/AppKit)

## Prerequisites

* CMake
* [Ninja](https://ninja-build.org/), in order to have the same build commands on all platforms
* [Ninja](https://ninja-build.org/), to have the same build commands on all platforms.

### Linux Dependencies

Install either Qt6 _or_ Qt5 development packages. The Linux backend requires
Qt Widgets, DBus, and Svg modules.

<div class="tabbed">

- <b class="tab-title">Arch</b>
```bash
sudo pacman -S libayatana-appindicator
# Qt6
sudo pacman -S qt6-base qt6-svg

# Qt5
sudo pacman -S qt5-base qt5-svg
```

- <b class="tab-title">Debian/Ubuntu</b>
```bash
sudo apt install libappindicator3-dev
# Qt6
sudo apt install qt6-base-dev qt6-svg-dev

# Qt5
sudo apt install qtbase5-dev libqt5svg5-dev
```

- <b class="tab-title">Fedora</b>
```bash
sudo dnf install libappindicator-gtk3-devel
# Qt6
sudo dnf install qt6-qtbase-devel qt6-qtsvg-devel

# Qt5
sudo dnf install qt5-qtbase-devel qt5-qtsvg-devel
```

</div>
Expand Down
Loading
Loading