From e4e52353e2757d40deefd60cbc6bbba00c53a028 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Tue, 30 Jul 2024 20:48:10 -0400 Subject: [PATCH] docs: add doxygen docs --- .codeql-prebuild-cpp-Linux.sh | 9 +- .codeql-prebuild-cpp-Windows.sh | 9 +- .codeql-prebuild-cpp-macOS.sh | 9 +- .github/workflows/ci.yml | 30 ++++-- .gitignore | 3 + .gitmodules | 4 + .readthedocs.yaml | 40 ++++++++ CMakeLists.txt | 24 ++++- README.md | 90 ++++++++++++------ docs/Doxyfile | 39 ++++++++ ...enshot_macosx.png => screenshot_macos.png} | Bin src/example.c | 14 ++- src/tray.h | 62 ++++++++---- src/tray_darwin.m | 21 +++- src/tray_linux.c | 14 ++- src/tray_windows.c | 60 +++++++++--- third-party/doxyconfig | 1 + 17 files changed, 347 insertions(+), 82 deletions(-) create mode 100644 .readthedocs.yaml create mode 100644 docs/Doxyfile rename docs/images/{screenshot_macosx.png => screenshot_macos.png} (100%) create mode 160000 third-party/doxyconfig diff --git a/.codeql-prebuild-cpp-Linux.sh b/.codeql-prebuild-cpp-Linux.sh index 800a89e..f88d64e 100644 --- a/.codeql-prebuild-cpp-Linux.sh +++ b/.codeql-prebuild-cpp-Linux.sh @@ -20,9 +20,12 @@ sudo rm -rf /var/lib/apt/lists/* # build mkdir -p build -cd build || exit 1 -cmake -G Ninja .. -ninja +cmake \ + -DBUILD_DOCS=OFF \ + -B build \ + -G Ninja \ + -S . +ninja -C build # skip autobuild echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" diff --git a/.codeql-prebuild-cpp-Windows.sh b/.codeql-prebuild-cpp-Windows.sh index d32da0b..2406033 100644 --- a/.codeql-prebuild-cpp-Windows.sh +++ b/.codeql-prebuild-cpp-Windows.sh @@ -13,9 +13,12 @@ pacman --noconfirm -S \ # build mkdir -p build -cd build || exit 1 -cmake -G Ninja .. -ninja +cmake \ + -DBUILD_DOCS=OFF \ + -B build \ + -G Ninja \ + -S . +ninja -C build # skip autobuild echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" diff --git a/.codeql-prebuild-cpp-macOS.sh b/.codeql-prebuild-cpp-macOS.sh index 0f1aeeb..f9fbba1 100644 --- a/.codeql-prebuild-cpp-macOS.sh +++ b/.codeql-prebuild-cpp-macOS.sh @@ -8,9 +8,12 @@ brew install \ # build mkdir -p build -cd build || exit 1 -cmake -G Ninja .. -ninja +cmake \ + -DBUILD_DOCS=OFF \ + -B build \ + -G Ninja \ + -S . +ninja -C build # skip autobuild echo "skip_autobuild=true" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38e4f8c..9eaa651 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,10 @@ jobs: run: | brew install \ cmake \ - ninja + doxygen \ + graphviz \ + ninja \ + node - name: Setup Dependencies Windows if: runner.os == 'Windows' @@ -73,9 +76,12 @@ jobs: msystem: ucrt64 update: true install: >- + doxygen mingw-w64-ucrt-x86_64-binutils mingw-w64-ucrt-x86_64-cmake + mingw-w64-ucrt-x86_64-graphviz mingw-w64-ucrt-x86_64-ninja + mingw-w64-ucrt-x86_64-nodejs mingw-w64-ucrt-x86_64-toolchain - name: Setup python @@ -101,20 +107,32 @@ jobs: - name: Build run: | mkdir -p build - cd build - cmake -DCMAKE_BUILD_TYPE:STRING=Debug -G Ninja .. - ninja + + if [ "${{ runner.os }}" = "Linux" ]; then + # Doxygen from Ubuntu is too old, need Doxygen >= 1.10 + DOCS=OFF + else + DOCS=ON + fi + + cmake \ + -DBUILD_DOCS=${DOCS} \ + -DCMAKE_BUILD_TYPE:STRING=Debug \ + -B build \ + -G Ninja \ + -S . + ninja -C build - name: Run tests id: test - working-directory: build + working-directory: build/tests run: | if [ "${{ runner.os }}" = "Linux" ]; then export DISPLAY=:1 Xvfb ${DISPLAY} -screen 0 1024x768x24 & fi - ./tests/test_tray --gtest_color=yes + ./test_tray --gtest_color=yes - name: Generate gcov report # any except canceled or skipped diff --git a/.gitignore b/.gitignore index 5073c82..fd5b8a8 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ # build directories build/ cmake-*/ + +# doxyconfig +docs/*-doxyconfig* diff --git a/.gitmodules b/.gitmodules index 6cbf29c..e3099df 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ +[submodule "third-party/doxyconfig"] + path = third-party/doxyconfig + url = https://github.com/LizardByte/doxyconfig.git + branch = master [submodule "third-party/googletest"] path = third-party/googletest url = https://github.com/google/googletest.git diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..a2c132d --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,40 @@ +--- +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +build: + os: ubuntu-24.04 + tools: + python: "miniconda-latest" + commands: + # because we are overriding the build commands, we need to setup the environment ourselves + - cat third-party/doxyconfig/environment.yml + - conda env create --quiet --name ${READTHEDOCS_VERSION} --file third-party/doxyconfig/environment.yml + - npm install "@fortawesome/fontawesome-free" + - mkdir -p ${READTHEDOCS_OUTPUT}html/assets/fontawesome/css + - mkdir -p ${READTHEDOCS_OUTPUT}html/assets/fontawesome/js + - cp node_modules/@fortawesome/fontawesome-free/css/all.min.css ${READTHEDOCS_OUTPUT}html/assets/fontawesome/css + - cp node_modules/@fortawesome/fontawesome-free/js/all.min.js ${READTHEDOCS_OUTPUT}html/assets/fontawesome/js + - cp -r node_modules/@fortawesome/fontawesome-free/webfonts ${READTHEDOCS_OUTPUT}html/assets/fontawesome/ + - | + wget "https://raw.githubusercontent.com/LizardByte/.github/master/branding/logos/favicon.ico" \ + -O ${READTHEDOCS_OUTPUT}lizardbyte.ico + - | + wget "https://raw.githubusercontent.com/LizardByte/.github/master/branding/logos/logo-128x128.png" \ + -O ${READTHEDOCS_OUTPUT}lizardbyte.png + - cp ./third-party/doxyconfig/Doxyfile ./docs/Doxyfile-doxyconfig + - cp ./third-party/doxyconfig/header.html ./docs/header-doxyconfig.html + - cat ./docs/Doxyfile >> ./docs/Doxyfile-doxyconfig + - cd docs && doxygen Doxyfile-doxyconfig + +# using conda, we can get newer doxygen and graphviz than ubuntu provide +# https://github.com/readthedocs/readthedocs.org/issues/8151#issuecomment-890359661 +conda: + environment: third-party/doxyconfig/environment.yml + +submodules: + include: all + recursive: true diff --git a/CMakeLists.txt b/CMakeLists.txt index 1df79fe..52567ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,11 @@ +# +# Project configuration +# cmake_minimum_required(VERSION 3.13 FATAL_ERROR) # target_link_directories - -project(tray - LANGUAGES C - DESCRIPTION "A cross-platform system tray library") +project(tray VERSION 0.0.0 + DESCRIPTION "A cross-platform system tray library" + HOMEPAGE_URL "https://app.lizardbyte.dev" + LANGUAGES C) set(PROJECT_LICENSE "MIT") @@ -11,11 +14,22 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 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") -# options +# +# Project optional configuration +# +option(BUILD_DOCS "Build documentation" ON) option(BUILD_TESTS "Build tests" ON) +# +# Documentation +# +if(BUILD_DOCS) + add_subdirectory(third-party/doxyconfig docs) +endif() + # Generate 'compile_commands.json' for clang_complete set(CMAKE_COLOR_MAKEFILE ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/README.md b/README.md index 7359deb..5bf571f 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,90 @@ -# Cross-platform Linux/macOS/Windows Tray +# Overview -[![codecov](https://img.shields.io/codecov/c/gh/LizardByte/tray?token=HSX66JNEOL&style=for-the-badge&logo=codecov&label=codecov)](https://codecov.io/gh/LizardByte/tray) +[![GitHub Workflow Status (CI)](https://img.shields.io/github/actions/workflow/status/lizardbyte/tray/ci.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/tray/actions/workflows/ci.yml?query=branch%3Amaster) +[![Codecov](https://img.shields.io/codecov/c/gh/LizardByte/tray?token=HSX66JNEOL&style=for-the-badge&logo=codecov&label=codecov)](https://codecov.io/gh/LizardByte/tray) +[![GitHub stars](https://img.shields.io/github/stars/lizardbyte/tray.svg?logo=github&style=for-the-badge)](https://github.com/LizardByte/tray) - +## 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](https://github.com/dmikushin/tray) and is intended to add additional features required for our own +[Sunshine](https://github.com/LizardByte/Sunshine) project. -Cross-platform, super tiny C99 implementation of a system tray icon with a popup menu. +This fork adds the following features: -Works well on: +- 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 + +## 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/Gtk (libayatana-appindicator3 or libappindicator3) * Windows XP or newer (shellapi.h) * MacOS (Cocoa/AppKit) -The code is C++ friendly and will compile fine in C++98 and up. +## Prerequisites -This fork is intended to bring together the [original work of Serge Zaitsev](https://github.com/zserge/tray) and the most interesting forks and PRs of respectable contributors: +* CMake +* [Ninja](https://ninja-build.org/), in order to have the same build commands on all platforms -* [Only process messages coming from the tray window on Windows](https://github.com/zserge/tray/pull/18) -* [Become C++-friendly](https://github.com/zserge/tray/pull/16) -* [Fix all menu items have a check box](https://github.com/zserge/tray/pull/11) -* [Add support for tooltip](https://github.com/zserge/tray/pull/11) -* Darwin implementation translated from C to Objective C adapted from [@trevex fork](https://github.com/trevex/tray) +### Linux Dependencies -## Prerequisites +
-* CMake -* [Ninja](https://ninja-build.org/), in order to have the same build commands on all platforms -* AppIndicator on Linux: +- Arch + ```bash + sudo pacman -S libayatana-appindicator + ``` -``` -sudo apt install libappindicator3-dev -``` +- Debian/Ubuntu + ```bash + sudo apt install libappindicator3-dev + ``` + +- Fedora + ```bash + sudo dnf install libappindicator-gtk3-devel + ``` + +
## Building -``` -mkdir build -cd build -cmake -G Ninja .. -ninja +```bash +mkdir -p build +cmake -G Ninja -B build -S . +ninja -C build ``` ## Demo Execute the `tray_example` application: +```bash +./build/tray_example ``` -./tray_example + +## Tests + +Execute the `tests` application: + +```bash +./build/tests/test_tray ``` ## API @@ -91,4 +125,4 @@ array must have text field set to NULL. ## License This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php), - so feel free to integrate it in your commercial products. +so feel free to integrate it in your commercial products. diff --git a/docs/Doxyfile b/docs/Doxyfile new file mode 100644 index 0000000..77aaefb --- /dev/null +++ b/docs/Doxyfile @@ -0,0 +1,39 @@ +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] + +# project metadata +DOCSET_BUNDLE_ID = dev.lizardbyte.tray +DOCSET_PUBLISHER_ID = dev.lizardbyte.tray.documentation +PROJECT_BRIEF = "Cross-platform, super tiny C99 implementation of a system tray icon with a popup menu and notifications." +PROJECT_NAME = tray + +# project specific settings +DOT_GRAPH_MAX_NODES = 50 +IMAGE_PATH = ../docs/images +INCLUDE_PATH = + +# files and directories to process +USE_MDFILE_AS_MAINPAGE = ../README.md +INPUT = ../README.md \ + ../third-party/doxyconfig/docs/source_code.md \ + ../src diff --git a/docs/images/screenshot_macosx.png b/docs/images/screenshot_macos.png similarity index 100% rename from docs/images/screenshot_macosx.png rename to docs/images/screenshot_macos.png diff --git a/src/example.c b/src/example.c index 9d7958d..7b3f81a 100644 --- a/src/example.c +++ b/src/example.c @@ -1,8 +1,12 @@ +/** + * @file src/example.c + * @brief Example usage of the tray library. + */ #include #include #if defined (_WIN32) || defined (_WIN64) -#define TRAY_WINAPI 1 +#define TRAY_WINAPI 1 ///< Use WinAPI. #elif defined (__linux__) || defined (linux) || defined (__linux) #define TRAY_APPINDICATOR 1 #elif defined (__APPLE__) || defined (__MACH__) @@ -18,8 +22,8 @@ #define TRAY_ICON1 "icon.png" #define TRAY_ICON2 "icon.png" #elif TRAY_WINAPI -#define TRAY_ICON1 "icon.ico" -#define TRAY_ICON2 "icon.ico" +#define TRAY_ICON1 "icon.ico" ///< Path to first icon. +#define TRAY_ICON2 "icon.ico" ///< Path to second icon. #endif static struct tray tray; @@ -92,6 +96,10 @@ static struct tray tray = { {.text = NULL}}, }; +/** + * @brief Main entry point. + * @return 0 on success, 1 on error. + */ int main() { if (tray_init(&tray) < 0) { printf("failed to create tray\n"); diff --git a/src/tray.h b/src/tray.h index c94854e..fb535f0 100644 --- a/src/tray.h +++ b/src/tray.h @@ -1,3 +1,7 @@ +/** + * @file src/tray.h + * @brief Definition of the tray API. + */ #ifndef TRAY_H #define TRAY_H @@ -5,41 +9,67 @@ extern "C" { #endif +/** + * @brief Tray menu item. + */ struct tray_menu; +/** + * @brief Tray icon. + */ struct tray { - const char *icon; - const char *tooltip; - const char *notification_icon; - const char *notification_text; - const char *notification_title; - void (*notification_cb)(); - struct tray_menu *menu; - const int iconPathCount; - const char *allIconPaths[]; + const char *icon; ///< Icon to display. + const char *tooltip; ///< Tooltip to display. + const char *notification_icon; ///< Icon to display in the notification. + const char *notification_text; ///< Text to display in the notification. + const char *notification_title; ///< Title to display in the notification. + void (*notification_cb)(); ///< Callback to invoke when the notification is clicked. + struct tray_menu *menu; ///< Menu items. + const int iconPathCount; ///< Number of icon paths. + const char *allIconPaths[]; ///< Array of icon paths. }; +/** + * @brief Tray menu item. + */ struct tray_menu { - const char *text; - int disabled; - int checked; - int checkbox; + const char *text; ///< Text to display. + int disabled; ///< Whether the item is disabled. + int checked; ///< Whether the item is checked. + int checkbox; ///< Whether the item is a checkbox. - void (*cb)(struct tray_menu *); - void *context; + void (*cb)(struct tray_menu *); ///< Callback to invoke when the item is clicked. + void *context; ///< Context to pass to the callback. - struct tray_menu *submenu; + struct tray_menu *submenu; ///< Submenu items. }; +/** + * @brief Create tray icon. + * @param tray The tray to initialize. + * @return 0 on success, -1 on error. + */ int tray_init(struct tray *tray); +/** + * @brief Run one iteration of the UI loop. + * @param blocking Whether to block the call or not. + * @return 0 on success, -1 if tray_exit() was called. + */ int tray_loop(int blocking); +/** + * @brief Update the tray icon and menu. + * @param tray The tray to update. + */ void tray_update(struct tray *tray); +/** + * @brief Terminate UI loop. + */ void tray_exit(void); diff --git a/src/tray_darwin.m b/src/tray_darwin.m index 9865bd6..b510e39 100644 --- a/src/tray_darwin.m +++ b/src/tray_darwin.m @@ -1,10 +1,29 @@ +/** + * @file src/tray_darwin.m + * @brief System tray implementation for macOS. + */ +// header include #include "tray.h" -#include + +// system includes #include +// lib includes +#include + +/** + * @class AppDelegate + * @brief The application delegate that handles menu actions. + */ @interface AppDelegate: NSObject +/** + * @brief Callback function for menu item actions. + * @param sender The object that sent the action message. + * @return void + */ - (IBAction)menuCallback:(id)sender; @end + @implementation AppDelegate { } - (IBAction)menuCallback:(id)sender { diff --git a/src/tray_linux.c b/src/tray_linux.c index 7faa3a2..a85a6d5 100644 --- a/src/tray_linux.c +++ b/src/tray_linux.c @@ -1,18 +1,26 @@ +/** + * @file src/tray_linux.c + * @brief System tray implementation for Linux. + */ +// header include #include "tray.h" + +// system includes #include #include #include + +// lib includes #ifdef TRAY_AYATANA_APPINDICATOR #include #elif TRAY_LEGACY_APPINDICATOR #include #endif #ifndef IS_APP_INDICATOR -#define IS_APP_INDICATOR APP_IS_INDICATOR +#define IS_APP_INDICATOR APP_IS_INDICATOR ///< Define IS_APP_INDICATOR for app-indicator compatibility. #endif - #include -#define TRAY_APPINDICATOR_ID "tray-id" +#define TRAY_APPINDICATOR_ID "tray-id" ///< Tray appindicator ID. static bool async_update_pending = false; static pthread_cond_t async_update_cv = PTHREAD_COND_INITIALIZER; diff --git a/src/tray_windows.c b/src/tray_windows.c index cf92da7..cbea316 100644 --- a/src/tray_windows.c +++ b/src/tray_windows.c @@ -1,22 +1,35 @@ +/** + * @file src/tray_windows.c + * @brief System tray implementation for Windows. + */ +// header include +#include "tray.h" + +// system includes #include #include -#include "tray.h" -#define WM_TRAY_CALLBACK_MESSAGE (WM_USER + 1) -#define WC_TRAY_CLASS_NAME "TRAY" -#define ID_TRAY_FIRST 1000 +#define WM_TRAY_CALLBACK_MESSAGE (WM_USER + 1) ///< Tray callback message. +#define WC_TRAY_CLASS_NAME "TRAY" ///< Tray window class name. +#define ID_TRAY_FIRST 1000 ///< First tray identifier. +/** + * @brief Icon information. + */ struct icon_info { - const char *path; - HICON icon; - HICON large_icon; - HICON notification_icon; + const char *path; ///< Path to the icon + HICON icon; ///< Regular icon + HICON large_icon; ///< Large icon + HICON notification_icon; ///< Notification icon }; +/** + * @brief Icon type. + */ enum IconType { - REGULAR = 1, - LARGE, - NOTIFICATION + REGULAR = 1, ///< Regular icon + LARGE, ///< Large icon + NOTIFICATION ///< Notification icon }; static WNDCLASSEX wc; @@ -108,6 +121,11 @@ static HMENU _tray_menu(struct tray_menu *m, UINT *id) { return hmenu; } +/** + * @brief Create icon information. + * @param path Path to the icon. + * @return Icon information. + */ struct icon_info _create_icon_info(const char * path) { struct icon_info info; info.path = strdup(path); @@ -121,6 +139,11 @@ struct icon_info _create_icon_info(const char * path) { return info; } +/** + * @brief Initialize icon cache. + * @param paths Paths to the icons. + * @param count Number of paths. + */ void _init_icon_cache(const char ** paths, int count) { icon_info_count = count; icon_infos = malloc(sizeof(struct icon_info) * icon_info_count); @@ -130,6 +153,9 @@ void _init_icon_cache(const char ** paths, int count) { } } +/** + * @brief Destroy icon cache. + */ void _destroy_icon_cache() { for (int i = 0; i < icon_info_count; ++i) { DestroyIcon(icon_infos[i].icon); @@ -143,6 +169,12 @@ void _destroy_icon_cache() { icon_info_count = 0; } +/** + * @brief Fetch cached icon. + * @param icon_record Icon record. + * @param icon_type Icon type. + * @return Icon. + */ HICON _fetch_cached_icon(struct icon_info *icon_record, enum IconType icon_type) { switch (icon_type) { case REGULAR: @@ -154,6 +186,12 @@ HICON _fetch_cached_icon(struct icon_info *icon_record, enum IconType icon_type) } } +/** + * @brief Fetch icon. + * @param path Path to the icon. + * @param icon_type Icon type. + * @return Icon. + */ HICON _fetch_icon(const char * path, enum IconType icon_type) { // Find a cached icon by path for (int i = 0; i < icon_info_count; ++i) { diff --git a/third-party/doxyconfig b/third-party/doxyconfig new file mode 160000 index 0000000..671b494 --- /dev/null +++ b/third-party/doxyconfig @@ -0,0 +1 @@ +Subproject commit 671b494f3cbe8597a36d81869a864dc9fff497f4