Cross-platform, super tiny C99 implementation of a system tray icon with a popup menu and notifications. https://docs.lizardbyte.dev/projects/tray
  • C++ 63.2%
  • C 21.6%
  • CMake 9.7%
  • Objective-C 5.5%
Find a file
renovate[bot] 32e0774e56
chore(deps): update actions/setup-python action to v7 (#163)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-20 13:00:31 -04:00
.github chore(deps): update actions/setup-python action to v7 (#163) 2026-07-20 13:00:31 -04:00
.run chore: use lizardbyte-common in c++ (#158) 2026-06-30 18:07:43 -04:00
cmake feat: Cleanup Qt-based tray implementation (#121) 2026-05-23 09:38:56 -04:00
docs chore: use lizardbyte-common in c++ (#158) 2026-06-30 18:07:43 -04:00
icons fix(linux): migrate to qt tray (#104) 2026-03-29 13:57:38 -04:00
src fix(mac): always run tray_update() on the main thread (#157) 2026-06-30 18:29:31 -04:00
tests chore: use lizardbyte-common in c++ (#158) 2026-06-30 18:07:43 -04:00
third-party chore: use lizardbyte-common in c++ (#158) 2026-06-30 18:07:43 -04:00
.clang-format chore: update global workflows (#64) 2025-08-30 22:18:04 -04:00
.gitattributes trying to override linguist language detection 2017-01-12 13:41:01 +02:00
.gitignore build(python): migrate to pyproject.toml and uv tooling (#140) 2026-05-30 08:37:47 -04:00
.gitmodules chore: use lizardbyte-common in c++ (#158) 2026-06-30 18:07:43 -04:00
.readthedocs.yaml Bump third-party/doxyconfig from 671b494 to 6d145da (#23) 2024-08-07 18:13:05 -04:00
.sonarcloud.properties chore: add sonar property files (#162) 2026-07-13 10:27:31 -04:00
CMakeLists.txt feat: Cleanup Qt-based tray implementation (#121) 2026-05-23 09:38:56 -04:00
LICENSE Initial commit 2017-01-09 16:40:06 +02:00
README.md chore: use lizardbyte-common in c++ (#158) 2026-06-30 18:07:43 -04:00
renovate.json chore: update global workflows (#124) 2026-05-15 17:51:42 -04:00
sonar-project.properties chore: add sonar property files (#162) 2026-07-13 10:27:31 -04:00
tray.svg chore: use lizardbyte-common in c++ (#158) 2026-06-30 18:07:43 -04:00

tray icon

tray

Cross-platform implementation of a system tray icon with a popup menu and notifications.

GitHub stars GitHub Workflow Status (CI) Codecov SonarCloud

Overview

About

Cross-platform, super tiny C99 implementation of a system tray icon with a popup menu and notifications.

The code is C++ friendly and will compile fine in C++98 and up. This is a fork of dmikushin/tray and is intended to add additional features required for our own Sunshine project.

This fork adds the following features:

  • system tray notifications
  • unit tests
  • code coverage
  • refactored code, e.g., moved source code into the src directory
  • doxygen documentation and readthedocs configuration

🖼️ Screenshots

  • Linux
    ![linux](docs/images/screenshot_linux.png)
  • macOS
    ![macOS](docs/images/screenshot_macos.png)
  • Windows
    ![windows](docs/images/screenshot_windows.png)

🖥️ Supported platforms

  • Linux/Qt (Qt5 or Qt6 Widgets)
  • Windows XP or newer (shellapi.h)
  • MacOS (Cocoa/AppKit)

📋 Prerequisites

  • CMake
  • Ninja, to have the same build commands on all platforms.

Linux Dependencies

Install either Qt6 or Qt5 as well as libnotify development packages. The Linux backend requires libnotify and Qt Widgets+Svg modules.

  • Arch

    # Qt6
    sudo pacman -S qt6-base qt6-svg libnotify
    
    # Qt5
    sudo pacman -S qt5-base qt5-svg libnotify
    
  • Debian/Ubuntu

    # Qt6
    sudo apt install qt6-base-dev qt6-svg-dev libnotify-dev
    
    # Qt5
    sudo apt install qtbase5-dev libqt5svg5-dev libnotify-dev
    
  • Fedora

    # Qt6
    sudo dnf install qt6-qtbase-devel qt6-qtsvg-devel libnotify-devel
    
    # Qt5
    sudo dnf install qt5-qtbase-devel qt5-qtsvg-devel libnotify-devel
    

🛠️ Building

mkdir -p build
cmake -G Ninja -B build -S .
ninja -C build

⚙️ Python Tooling

Install uv and initialize the shared tooling submodule:

git submodule update --init --recursive third-party/lizardbyte-common
uv run --project third-party/lizardbyte-common --locked --only-group lint-c \
  python third-party/lizardbyte-common/scripts/update_clang_format.py

▶️ Demo

Execute the tray_example application:

./build/tray_example

Tests

Execute the tests application:

./build/tests/test_tray

📚 API

Tray structure defines an icon and a menu. Menu is a NULL-terminated array of items. Menu item defines menu text, menu checked and disabled (grayed) flags and a callback with some optional context pointer.

struct tray {
  char *icon;
  struct tray_menu *menu;
};

struct tray_menu {
  char *text;
  int disabled;
  int checked;

  void (*cb)(struct tray_menu *);
  void *context;

  struct tray_menu *submenu;
};
  • int tray_init(struct tray *) - creates tray icon. Returns -1 if tray icon/menu can't be created.
  • void tray_update(struct tray *) - updates tray icon and menu.
  • int tray_loop(int blocking) - runs one iteration of the UI loop. Returns -1 if tray_exit() has been called.
  • void tray_exit() - terminates UI loop.

All functions are meant to be called from the UI thread only.

Menu arrays must be terminated with a NULL item, e.g. the last item in the array must have text field set to NULL.

📄 License

This software is distributed under MIT license, so feel free to integrate it in your commercial products.

[TOC]