diff --git a/.github/workflows/common-lint.yml b/.github/workflows/common-lint.yml new file mode 100644 index 0000000..4a62b24 --- /dev/null +++ b/.github/workflows/common-lint.yml @@ -0,0 +1,245 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +# Common linting. + +name: common lint + +on: + pull_request: + branches: [master] + types: [opened, synchronize, reopened] + +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + lint: + name: Common Lint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: | + python -m pip install --upgrade \ + pip \ + setuptools \ + wheel \ + cmakelang \ + flake8 \ + nb-clean \ + nbqa[toolchain] + + - name: C++ - find files + id: cpp_files + run: | + # find files + found_files=$(find . -type f \ + -iname "*.c" -o \ + -iname "*.cpp" -o \ + -iname "*.h" -o \ + -iname "*.hpp" -o \ + -iname "*.m" -o \ + -iname "*.mm" \ + ) + ignore_files=$(find . -type f -iname ".clang-format-ignore") + + # Loop through each C++ file + for file in $found_files; do + for ignore_file in $ignore_files; do + ignore_directory=$(dirname "$ignore_file") + # if directory of ignore_file is beginning of file + if [[ "$file" == "$ignore_directory"* ]]; then + echo "ignoring file: ${file}" + found_files="${found_files//${file}/}" + break 1 + fi + done + done + + # remove empty lines + found_files=$(echo "$found_files" | sed '/^\s*$/d') + + echo "found cpp files: ${found_files}" + + # do not quote to keep this as a single line + echo found_files=${found_files} >> $GITHUB_OUTPUT + + - name: C++ - Clang format lint + if: always() && steps.cpp_files.outputs.found_files + uses: DoozyX/clang-format-lint-action@v0.18 + with: + source: ${{ steps.cpp_files.outputs.found_files }} + extensions: 'c,cpp,h,hpp,m,mm' + style: file + inplace: false + + - name: CMake - find files + id: cmake_files + if: always() + run: | + # find files + found_files=$(find . -type f -iname "CMakeLists.txt" -o -iname "*.cmake") + ignore_files=$(find . -type f -iname ".cmake-lint-ignore") + + # Loop through each C++ file + for file in $found_files; do + for ignore_file in $ignore_files; do + ignore_directory=$(dirname "$ignore_file") + # if directory of ignore_file is beginning of file + if [[ "$file" == "$ignore_directory"* ]]; then + echo "ignoring file: ${file}" + found_files="${found_files//${file}/}" + break 1 + fi + done + done + + # remove empty lines + found_files=$(echo "$found_files" | sed '/^\s*$/d') + + echo "found cmake files: ${found_files}" + + # do not quote to keep this as a single line + echo found_files=${found_files} >> $GITHUB_OUTPUT + + - name: CMake - cmake-lint + if: always() && steps.cmake_files.outputs.found_files + run: | + cmake-lint --line-width 120 --tab-size 4 ${{ steps.cmake_files.outputs.found_files }} + + - name: Docker - find files + id: dokcer_files + if: always() + run: | + found_files=$(find . -type f -iname "Dockerfile" -o -iname "*.dockerfile") + + echo "found_files: ${found_files}" + + # do not quote to keep this as a single line + echo found_files=${found_files} >> $GITHUB_OUTPUT + + - name: Docker - hadolint + if: always() && steps.dokcer_files.outputs.found_files + run: | + docker pull hadolint/hadolint + + # create hadolint config file + cat < .hadolint.yaml + --- + ignored: + - DL3008 + - DL3013 + - DL3016 + - DL3018 + - DL3028 + - DL3059 + EOF + + failed=0 + failed_files="" + + for file in ${{ steps.dokcer_files.outputs.found_files }}; do + echo "::group::${file}" + docker run --rm -i \ + -e "NO_COLOR=0" \ + -e "HADOLINT_VERBOSE=1" \ + -v $(pwd)/.hadolint.yaml:/.config/hadolint.yaml \ + hadolint/hadolint < $file || { + failed=1 + failed_files="$failed_files $file" + } + echo "::endgroup::" + done + + if [ $failed -ne 0 ]; then + echo "::error:: hadolint failed for the following files: $failed_files" + exit 1 + fi + + - name: Python - flake8 + if: always() + run: | + python -m flake8 \ + --color=always \ + --verbose + + - name: Python - nbqa flake8 + if: always() + run: | + python -m nbqa flake8 \ + --color=always \ + --verbose \ + . + + - name: Python - nb-clean + if: always() + run: | + output=$(find . -name '*.ipynb' -exec nb-clean check {} \;) + + # fail if there are any issues + if [ -n "$output" ]; then + echo "$output" + exit 1 + fi + + - name: YAML - find files + id: yaml_files + if: always() + run: | + # space separated list of files + FILES=.clang-format + + # empty placeholder + found_files="" + + for FILE in ${FILES}; do + if [ -f "$FILE" ] + then + found_files="$found_files $FILE" + fi + done + + echo "found_files=${found_files}" >> $GITHUB_OUTPUT + + - name: YAML - yamllint + id: yamllint + if: always() + uses: ibiqlik/action-yamllint@v3 + with: + # https://yamllint.readthedocs.io/en/stable/configuration.html#default-configuration + config_data: | + extends: default + rules: + comments: + level: error + document-start: + level: error + line-length: + max: 120 + new-line-at-end-of-file: + level: error + new-lines: + type: unix + truthy: + # GitHub uses "on" for workflow event triggers + # .clang-format file has options of "Yes" "No" that will be caught by this, so changed to "warning" + allowed-values: ['true', 'false', 'on'] + check-keys: true + level: warning + file_or_dir: . ${{ steps.yaml_files.outputs.found_files }} + + - name: YAML - log + if: always() && steps.yamllint.outcome == 'failure' + run: | + cat "${{ steps.yamllint.outputs.logfile }}" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/cpp-lint.yml b/.github/workflows/cpp-lint.yml deleted file mode 100644 index 96cb1d0..0000000 --- a/.github/workflows/cpp-lint.yml +++ /dev/null @@ -1,120 +0,0 @@ ---- -# This action is centrally managed in https://github.com//.github/ -# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in -# the above-mentioned repo. - -# Lint c++ source files and cmake files. - -name: C++ Lint - -on: - pull_request: - branches: [master] - types: [opened, synchronize, reopened] - -concurrency: - group: "${{ github.workflow }}-${{ github.ref }}" - cancel-in-progress: true - -jobs: - clang-format: - name: Clang Format Lint - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Find cpp files - id: find_files - run: | - # find files - found_files=$(find . -type f -iname "*.cpp" -o -iname "*.h" -o -iname "*.m" -o -iname "*.mm") - ignore_files=$(find . -type f -iname ".clang-format-ignore") - - # Loop through each C++ file - for file in $found_files; do - for ignore_file in $ignore_files; do - ignore_directory=$(dirname "$ignore_file") - # if directory of ignore_file is beginning of file - if [[ "$file" == "$ignore_directory"* ]]; then - echo "ignoring file: ${file}" - found_files="${found_files//${file}/}" - break 1 - fi - done - done - - # remove empty lines - found_files=$(echo "$found_files" | sed '/^\s*$/d') - - echo "found cpp files: ${found_files}" - - # do not quote to keep this as a single line - echo found_files=${found_files} >> $GITHUB_OUTPUT - - - name: Clang format lint - if: ${{ steps.find_files.outputs.found_files }} - uses: DoozyX/clang-format-lint-action@v0.18 - with: - source: ${{ steps.find_files.outputs.found_files }} - extensions: 'cpp,h,m,mm' - clangFormatVersion: 16 - style: file - inplace: false - - - name: Upload Artifacts - if: failure() - uses: actions/upload-artifact@v4 - with: - name: clang-format-fixes - path: ${{ steps.find_files.outputs.found_files }} - - cmake-lint: - name: CMake Lint - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip setuptools cmakelang - - - name: Find cmake files - id: find_files - run: | - # find files - found_files=$(find . -type f -iname "CMakeLists.txt" -o -iname "*.cmake") - ignore_files=$(find . -type f -iname ".cmake-lint-ignore") - - # Loop through each C++ file - for file in $found_files; do - for ignore_file in $ignore_files; do - ignore_directory=$(dirname "$ignore_file") - # if directory of ignore_file is beginning of file - if [[ "$file" == "$ignore_directory"* ]]; then - echo "ignoring file: ${file}" - found_files="${found_files//${file}/}" - break 1 - fi - done - done - - # remove empty lines - found_files=$(echo "$found_files" | sed '/^\s*$/d') - - echo "found cmake files: ${found_files}" - - # do not quote to keep this as a single line - echo found_files=${found_files} >> $GITHUB_OUTPUT - - - name: Test with cmake-lint - run: | - cmake-lint --line-width 120 --tab-size 4 ${{ steps.find_files.outputs.found_files }} diff --git a/.github/workflows/python-flake8.yml b/.github/workflows/python-flake8.yml deleted file mode 100644 index 6077a95..0000000 --- a/.github/workflows/python-flake8.yml +++ /dev/null @@ -1,62 +0,0 @@ ---- -# This action is centrally managed in https://github.com//.github/ -# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in -# the above-mentioned repo. - -# Lint python files with flake8. - -name: flake8 - -on: - pull_request: - branches: [master] - types: [opened, synchronize, reopened] - -concurrency: - group: "${{ github.workflow }}-${{ github.ref }}" - cancel-in-progress: true - -jobs: - flake8: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 # https://github.com/actions/setup-python - with: - python-version: '3.12' - - - name: Install dependencies - run: | - python -m pip install --upgrade \ - pip \ - setuptools \ - wheel \ - flake8 \ - nb-clean \ - nbqa[toolchain] - - - name: Test with flake8 - run: | - python -m flake8 \ - --color=always \ - --verbose - - - name: Test with nbqa - run: | - python -m nbqa flake8 \ - --color=always \ - --verbose \ - . - - - name: Test with nb-clean - run: | - output=$(find . -name '*.ipynb' -exec nb-clean check {} \;) - - # fail if there are any issues - if [ -n "$output" ]; then - echo "$output" - exit 1 - fi diff --git a/.github/workflows/yaml-lint.yml b/.github/workflows/yaml-lint.yml deleted file mode 100644 index 023b836..0000000 --- a/.github/workflows/yaml-lint.yml +++ /dev/null @@ -1,66 +0,0 @@ ---- -# This action is centrally managed in https://github.com//.github/ -# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in -# the above-mentioned repo. - -# Lint yaml files. - -name: yaml lint - -on: - pull_request: - branches: [master] - types: [opened, synchronize, reopened] - -concurrency: - group: "${{ github.workflow }}-${{ github.ref }}" - cancel-in-progress: true - -jobs: - yaml-lint: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Find additional files - id: find-files - run: | - # space separated list of files - FILES=.clang-format - - # empty placeholder - FOUND="" - - for FILE in ${FILES}; do - if [ -f "$FILE" ] - then - FOUND="$FOUND $FILE" - fi - done - - echo "found=${FOUND}" >> $GITHUB_OUTPUT - - - name: yaml lint - id: yaml-lint - uses: ibiqlik/action-yamllint@v3 - with: - # https://yamllint.readthedocs.io/en/stable/configuration.html#default-configuration - config_data: | - extends: default - rules: - comments: - level: error - line-length: - max: 120 - truthy: - # GitHub uses "on" for workflow event triggers - # .clang-format file has options of "Yes" "No" that will be caught by this, so changed to "warning" - allowed-values: ['true', 'false', 'on'] - check-keys: true - level: warning - file_or_dir: . ${{ steps.find-files.outputs.found }} - - - name: Log - run: | - cat "${{ steps.yaml-lint.outputs.logfile }}" >> $GITHUB_STEP_SUMMARY diff --git a/src/example.c b/src/example.c index 7b3f81a..9e745df 100644 --- a/src/example.c +++ b/src/example.c @@ -5,102 +5,108 @@ #include #include -#if defined (_WIN32) || defined (_WIN64) -#define TRAY_WINAPI 1 ///< Use WinAPI. -#elif defined (__linux__) || defined (linux) || defined (__linux) -#define TRAY_APPINDICATOR 1 -#elif defined (__APPLE__) || defined (__MACH__) -#define TRAY_APPKIT 1 +#if defined(_WIN32) || defined(_WIN64) + #define TRAY_WINAPI 1 ///< Use WinAPI. +#elif defined(__linux__) || defined(linux) || defined(__linux) + #define TRAY_APPINDICATOR 1 +#elif defined(__APPLE__) || defined(__MACH__) + #define TRAY_APPKIT 1 #endif #include "tray.h" #if TRAY_APPINDICATOR -#define TRAY_ICON1 "mail-message-new" -#define TRAY_ICON2 "mail-message-new" + #define TRAY_ICON1 "mail-message-new" + #define TRAY_ICON2 "mail-message-new" #elif TRAY_APPKIT -#define TRAY_ICON1 "icon.png" -#define TRAY_ICON2 "icon.png" + #define TRAY_ICON1 "icon.png" + #define TRAY_ICON2 "icon.png" #elif TRAY_WINAPI -#define TRAY_ICON1 "icon.ico" ///< Path to first icon. -#define TRAY_ICON2 "icon.ico" ///< Path to second icon. + #define TRAY_ICON1 "icon.ico" ///< Path to first icon. + #define TRAY_ICON2 "icon.ico" ///< Path to second icon. #endif static struct tray tray; -static void toggle_cb(struct tray_menu *item) { +static void +toggle_cb(struct tray_menu *item) { printf("toggle cb\n"); item->checked = !item->checked; tray_update(&tray); } -static void hello_cb(struct tray_menu *item) { - (void)item; +static void +hello_cb(struct tray_menu *item) { + (void) item; printf("hello cb\n"); if (strcmp(tray.icon, TRAY_ICON1) == 0) { tray.icon = TRAY_ICON2; - } else { + } + else { tray.icon = TRAY_ICON1; } tray_update(&tray); } -static void quit_cb(struct tray_menu *item) { - (void)item; +static void +quit_cb(struct tray_menu *item) { + (void) item; printf("quit cb\n"); tray_exit(); } -static void submenu_cb(struct tray_menu *item) { - (void)item; +static void +submenu_cb(struct tray_menu *item) { + (void) item; printf("submenu: clicked on %s\n", item->text); tray_update(&tray); } // Test tray init static struct tray tray = { - .icon = TRAY_ICON1, + .icon = TRAY_ICON1, #if TRAY_WINAPI - .tooltip = "Tray", + .tooltip = "Tray", #endif - .menu = - (struct tray_menu[]) { - {.text = "Hello", .cb = hello_cb}, - {.text = "Checked", .checked = 1, .checkbox = 1, .cb = toggle_cb}, - {.text = "Disabled", .disabled = 1}, - {.text = "-"}, - {.text = "SubMenu", - .submenu = - (struct tray_menu[]) { - {.text = "FIRST", .checked = 1, .checkbox = 1, .cb = submenu_cb}, - {.text = "SECOND", - .submenu = - (struct tray_menu[]) { - {.text = "THIRD", - .submenu = - (struct tray_menu[]) { - {.text = "7", .cb = submenu_cb}, - {.text = "-"}, - {.text = "8", .cb = submenu_cb}, - {.text = NULL}}}, - {.text = "FOUR", - .submenu = - (struct tray_menu[]) { - {.text = "5", .cb = submenu_cb}, - {.text = "6", .cb = submenu_cb}, - {.text = NULL}}}, - {.text = NULL}}}, - {.text = NULL}}}, - {.text = "-"}, - {.text = "Quit", .cb = quit_cb}, - {.text = NULL}}, + .menu = + (struct tray_menu[]) { + { .text = "Hello", .cb = hello_cb }, + { .text = "Checked", .checked = 1, .checkbox = 1, .cb = toggle_cb }, + { .text = "Disabled", .disabled = 1 }, + { .text = "-" }, + { .text = "SubMenu", + .submenu = + (struct tray_menu[]) { + { .text = "FIRST", .checked = 1, .checkbox = 1, .cb = submenu_cb }, + { .text = "SECOND", + .submenu = + (struct tray_menu[]) { + { .text = "THIRD", + .submenu = + (struct tray_menu[]) { + { .text = "7", .cb = submenu_cb }, + { .text = "-" }, + { .text = "8", .cb = submenu_cb }, + { .text = NULL } } }, + { .text = "FOUR", + .submenu = + (struct tray_menu[]) { + { .text = "5", .cb = submenu_cb }, + { .text = "6", .cb = submenu_cb }, + { .text = NULL } } }, + { .text = NULL } } }, + { .text = NULL } } }, + { .text = "-" }, + { .text = "Quit", .cb = quit_cb }, + { .text = NULL } }, }; /** * @brief Main entry point. * @return 0 on success, 1 on error. */ -int main() { +int +main() { if (tray_init(&tray) < 0) { printf("failed to create tray\n"); return 1; diff --git a/src/tray_linux.c b/src/tray_linux.c index a85a6d5..c0ec205 100644 --- a/src/tray_linux.c +++ b/src/tray_linux.c @@ -6,18 +6,18 @@ #include "tray.h" // system includes -#include -#include #include +#include +#include // lib includes #ifdef TRAY_AYATANA_APPINDICATOR -#include + #include #elif TRAY_LEGACY_APPINDICATOR -#include + #include #endif #ifndef IS_APP_INDICATOR -#define IS_APP_INDICATOR APP_IS_INDICATOR ///< Define IS_APP_INDICATOR for app-indicator compatibility. + #define IS_APP_INDICATOR APP_IS_INDICATOR ///< Define IS_APP_INDICATOR for app-indicator compatibility. #endif #include #define TRAY_APPINDICATOR_ID "tray-id" ///< Tray appindicator ID. @@ -27,28 +27,30 @@ static pthread_cond_t async_update_cv = PTHREAD_COND_INITIALIZER; static pthread_mutex_t async_update_mutex = PTHREAD_MUTEX_INITIALIZER; static AppIndicator *indicator = NULL; -static int loop_result = 0; +static int loop_result = 0; static NotifyNotification *currentNotification = NULL; -static void _tray_menu_cb(GtkMenuItem *item, gpointer data) { - (void)item; - struct tray_menu *m = (struct tray_menu *)data; +static void +_tray_menu_cb(GtkMenuItem *item, gpointer data) { + (void) item; + struct tray_menu *m = (struct tray_menu *) data; m->cb(m); } -static GtkMenuShell *_tray_menu(struct tray_menu *m) { - GtkMenuShell *menu = (GtkMenuShell *)gtk_menu_new(); - for(; m != NULL && m->text != NULL; m++) { +static GtkMenuShell * +_tray_menu(struct tray_menu *m) { + GtkMenuShell *menu = (GtkMenuShell *) gtk_menu_new(); + for (; m != NULL && m->text != NULL; m++) { GtkWidget *item; - if(strcmp(m->text, "-") == 0) { + if (strcmp(m->text, "-") == 0) { item = gtk_separator_menu_item_new(); } else { - if(m->submenu != NULL) { + if (m->submenu != NULL) { item = gtk_menu_item_new_with_label(m->text); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), GTK_WIDGET(_tray_menu(m->submenu))); } - else if(m->checkbox) { + else if (m->checkbox) { item = gtk_check_menu_item_new_with_label(m->text); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), !!m->checked); } @@ -56,7 +58,7 @@ static GtkMenuShell *_tray_menu(struct tray_menu *m) { item = gtk_menu_item_new_with_label(m->text); } gtk_widget_set_sensitive(item, !m->disabled); - if(m->cb != NULL) { + if (m->cb != NULL) { g_signal_connect(item, "activate", G_CALLBACK(_tray_menu_cb), m); } } @@ -66,43 +68,46 @@ static GtkMenuShell *_tray_menu(struct tray_menu *m) { return menu; } -int tray_init(struct tray *tray) { - if(gtk_init_check(0, NULL) == FALSE) { +int +tray_init(struct tray *tray) { + if (gtk_init_check(0, NULL) == FALSE) { return -1; } notify_init("tray-icon"); indicator = app_indicator_new(TRAY_APPINDICATOR_ID, tray->icon, APP_INDICATOR_CATEGORY_APPLICATION_STATUS); - if(indicator == NULL || !IS_APP_INDICATOR(indicator))return -1; + if (indicator == NULL || !IS_APP_INDICATOR(indicator)) return -1; app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE); tray_update(tray); return 0; } -int tray_loop(int blocking) { +int +tray_loop(int blocking) { gtk_main_iteration_do(blocking); return loop_result; } -static gboolean tray_update_internal(gpointer user_data) { +static gboolean +tray_update_internal(gpointer user_data) { struct tray *tray = user_data; - if(indicator != NULL && IS_APP_INDICATOR(indicator)){ + if (indicator != NULL && IS_APP_INDICATOR(indicator)) { app_indicator_set_icon_full(indicator, tray->icon, tray->icon); // GTK is all about reference counting, so previous menu should be destroyed // here app_indicator_set_menu(indicator, GTK_MENU(_tray_menu(tray->menu))); } - if(tray->notification_text != 0 && strlen(tray->notification_text) > 0 && notify_is_initted()) { - if(currentNotification != NULL && NOTIFY_IS_NOTIFICATION(currentNotification)){ - notify_notification_close(currentNotification,NULL); + if (tray->notification_text != 0 && strlen(tray->notification_text) > 0 && notify_is_initted()) { + if (currentNotification != NULL && NOTIFY_IS_NOTIFICATION(currentNotification)) { + notify_notification_close(currentNotification, NULL); g_object_unref(G_OBJECT(currentNotification)); } const char *notification_icon = tray->notification_icon != NULL ? tray->notification_icon : tray->icon; currentNotification = notify_notification_new(tray->notification_title, tray->notification_text, notification_icon); - if(currentNotification != NULL && NOTIFY_IS_NOTIFICATION(currentNotification)){ - if(tray->notification_cb != NULL){ - notify_notification_add_action(currentNotification,"default","Default",tray->notification_cb,NULL,NULL); + if (currentNotification != NULL && NOTIFY_IS_NOTIFICATION(currentNotification)) { + if (tray->notification_cb != NULL) { + notify_notification_add_action(currentNotification, "default", "Default", tray->notification_cb, NULL, NULL); } notify_notification_show(currentNotification, NULL); } @@ -116,7 +121,8 @@ static gboolean tray_update_internal(gpointer user_data) { return G_SOURCE_REMOVE; } -void tray_update(struct tray *tray) { +void +tray_update(struct tray *tray) { // Perform the tray update on the tray loop thread, but block // in this thread to ensure none of the strings stored in the // tray icon struct go out of scope before the callback runs. @@ -147,16 +153,18 @@ void tray_update(struct tray *tray) { } } -static gboolean tray_exit_internal(gpointer user_data) { - if(currentNotification != NULL && NOTIFY_IS_NOTIFICATION(currentNotification)){ - int v = notify_notification_close(currentNotification,NULL); - if(v == TRUE)g_object_unref(G_OBJECT(currentNotification)); +static gboolean +tray_exit_internal(gpointer user_data) { + if (currentNotification != NULL && NOTIFY_IS_NOTIFICATION(currentNotification)) { + int v = notify_notification_close(currentNotification, NULL); + if (v == TRUE) g_object_unref(G_OBJECT(currentNotification)); } notify_uninit(); return G_SOURCE_REMOVE; } -void tray_exit(void) { +void +tray_exit(void) { // Wait for any pending callbacks to complete pthread_mutex_lock(&async_update_mutex); while (async_update_pending) { diff --git a/src/tray_windows.c b/src/tray_windows.c index cbea316..f2cebf3 100644 --- a/src/tray_windows.c +++ b/src/tray_windows.c @@ -7,7 +7,10 @@ // system includes #include +// clang-format off +// build fails if shellapi.h is included before windows.h #include +// clang-format on #define WM_TRAY_CALLBACK_MESSAGE (WM_USER + 1) ///< Tray callback message. #define WC_TRAY_CLASS_NAME "TRAY" ///< Tray window class name. @@ -42,43 +45,44 @@ static UINT wm_taskbarcreated; static struct icon_info *icon_infos; static int icon_info_count; -static LRESULT CALLBACK _tray_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, - LPARAM lparam) { +static LRESULT CALLBACK +_tray_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { - case WM_CLOSE: - DestroyWindow(hwnd); - return 0; - case WM_DESTROY: - PostQuitMessage(0); - return 0; - case WM_TRAY_CALLBACK_MESSAGE: - if (lparam == WM_LBUTTONUP || lparam == WM_RBUTTONUP) { - POINT p; - GetCursorPos(&p); - SetForegroundWindow(hwnd); - WORD cmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON | - TPM_RETURNCMD | TPM_NONOTIFY, - p.x, p.y, 0, hwnd, NULL); - SendMessage(hwnd, WM_COMMAND, cmd, 0); + case WM_CLOSE: + DestroyWindow(hwnd); return 0; - } else if(lparam == NIN_BALLOONUSERCLICK && notification_cb != NULL){ - notification_cb(); - } - break; - case WM_COMMAND: - if (wparam >= ID_TRAY_FIRST) { - MENUITEMINFO item = { - .cbSize = sizeof(MENUITEMINFO), .fMask = MIIM_ID | MIIM_DATA, - }; - if (GetMenuItemInfo(hmenu, wparam, FALSE, &item)) { - struct tray_menu *menu = (struct tray_menu *)item.dwItemData; - if (menu != NULL && menu->cb != NULL) { - menu->cb(menu); + case WM_DESTROY: + PostQuitMessage(0); + return 0; + case WM_TRAY_CALLBACK_MESSAGE: + if (lparam == WM_LBUTTONUP || lparam == WM_RBUTTONUP) { + POINT p; + GetCursorPos(&p); + SetForegroundWindow(hwnd); + WORD cmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, + p.x, p.y, 0, hwnd, NULL); + SendMessage(hwnd, WM_COMMAND, cmd, 0); + return 0; + } + else if (lparam == NIN_BALLOONUSERCLICK && notification_cb != NULL) { + notification_cb(); + } + break; + case WM_COMMAND: + if (wparam >= ID_TRAY_FIRST) { + MENUITEMINFO item = { + .cbSize = sizeof(MENUITEMINFO), + .fMask = MIIM_ID | MIIM_DATA, + }; + if (GetMenuItemInfo(hmenu, wparam, FALSE, &item)) { + struct tray_menu *menu = (struct tray_menu *) item.dwItemData; + if (menu != NULL && menu->cb != NULL) { + menu->cb(menu); + } } + return 0; } - return 0; - } - break; + break; } if (msg == wm_taskbarcreated) { @@ -89,12 +93,14 @@ static LRESULT CALLBACK _tray_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, return DefWindowProc(hwnd, msg, wparam, lparam); } -static HMENU _tray_menu(struct tray_menu *m, UINT *id) { +static HMENU +_tray_menu(struct tray_menu *m, UINT *id) { HMENU hmenu = CreatePopupMenu(); for (; m != NULL && m->text != NULL; m++, (*id)++) { if (strcmp(m->text, "-") == 0) { InsertMenu(hmenu, *id, MF_SEPARATOR, TRUE, ""); - } else { + } + else { MENUITEMINFO item; memset(&item, 0, sizeof(item)); item.cbSize = sizeof(MENUITEMINFO); @@ -112,8 +118,8 @@ static HMENU _tray_menu(struct tray_menu *m, UINT *id) { item.fState |= MFS_CHECKED; } item.wID = *id; - item.dwTypeData = (LPSTR)m->text; - item.dwItemData = (ULONG_PTR)m; + item.dwTypeData = (LPSTR) m->text; + item.dwItemData = (ULONG_PTR) m; InsertMenuItem(hmenu, *id, TRUE, &item); } @@ -126,7 +132,8 @@ static HMENU _tray_menu(struct tray_menu *m, UINT *id) { * @param path Path to the icon. * @return Icon information. */ -struct icon_info _create_icon_info(const char * path) { +struct icon_info +_create_icon_info(const char *path) { struct icon_info info; info.path = strdup(path); @@ -144,7 +151,8 @@ struct icon_info _create_icon_info(const char * path) { * @param paths Paths to the icons. * @param count Number of paths. */ -void _init_icon_cache(const char ** paths, int count) { +void +_init_icon_cache(const char **paths, int count) { icon_info_count = count; icon_infos = malloc(sizeof(struct icon_info) * icon_info_count); @@ -156,12 +164,13 @@ void _init_icon_cache(const char ** paths, int count) { /** * @brief Destroy icon cache. */ -void _destroy_icon_cache() { +void +_destroy_icon_cache() { for (int i = 0; i < icon_info_count; ++i) { DestroyIcon(icon_infos[i].icon); DestroyIcon(icon_infos[i].large_icon); DestroyIcon(icon_infos[i].notification_icon); - free((void*) icon_infos[i].path); + free((void *) icon_infos[i].path); } free(icon_infos); @@ -175,7 +184,8 @@ void _destroy_icon_cache() { * @param icon_type Icon type. * @return Icon. */ -HICON _fetch_cached_icon(struct icon_info *icon_record, enum IconType icon_type) { +HICON +_fetch_cached_icon(struct icon_info *icon_record, enum IconType icon_type) { switch (icon_type) { case REGULAR: return icon_record->icon; @@ -192,7 +202,8 @@ HICON _fetch_cached_icon(struct icon_info *icon_record, enum IconType icon_type) * @param icon_type Icon type. * @return Icon. */ -HICON _fetch_icon(const char * path, enum IconType icon_type) { +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) { if (strcmp(icon_infos[i].path, path) == 0) { @@ -209,7 +220,8 @@ HICON _fetch_icon(const char * path, enum IconType icon_type) { return _fetch_cached_icon(&icon_infos[icon_info_count - 1], icon_type); } -int tray_init(struct tray *tray) { +int +tray_init(struct tray *tray) { wm_taskbarcreated = RegisterWindowMessage("TaskbarCreated"); _init_icon_cache(tray->allIconPaths, tray->iconPathCount); @@ -241,11 +253,13 @@ int tray_init(struct tray *tray) { return 0; } -int tray_loop(int blocking) { +int +tray_loop(int blocking) { MSG msg; if (blocking) { GetMessage(&msg, hwnd, 0, 0); - } else { + } + else { PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE); } if (msg.message == WM_QUIT) { @@ -256,44 +270,45 @@ int tray_loop(int blocking) { return 0; } -void tray_update(struct tray *tray) { +void +tray_update(struct tray *tray) { UINT id = ID_TRAY_FIRST; HMENU prevmenu = hmenu; hmenu = _tray_menu(tray->menu, &id); - SendMessage(hwnd, WM_INITMENUPOPUP, (WPARAM)hmenu, 0); + SendMessage(hwnd, WM_INITMENUPOPUP, (WPARAM) hmenu, 0); HICON icon = _fetch_icon(tray->icon, REGULAR); - HICON largeIcon = tray->notification_icon != 0 - ? _fetch_icon(tray->notification_icon, NOTIFICATION) - : _fetch_icon(tray->icon, LARGE); + HICON largeIcon = tray->notification_icon != 0 ? _fetch_icon(tray->notification_icon, NOTIFICATION) : _fetch_icon(tray->icon, LARGE); if (icon != NULL) { nid.hIcon = icon; } - if(largeIcon != 0){ + if (largeIcon != 0) { nid.hBalloonIcon = largeIcon; nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; } - if(tray->tooltip != 0 && strlen(tray->tooltip) > 0) { + if (tray->tooltip != 0 && strlen(tray->tooltip) > 0) { strncpy(nid.szTip, tray->tooltip, sizeof(nid.szTip)); nid.uFlags |= NIF_TIP; } QUERY_USER_NOTIFICATION_STATE notification_state; HRESULT ns = SHQueryUserNotificationState(¬ification_state); int can_show_notifications = ns == S_OK && notification_state == QUNS_ACCEPTS_NOTIFICATIONS; - if(can_show_notifications == 1 && tray->notification_title != 0 && strlen(tray->notification_title) > 0){ + if (can_show_notifications == 1 && tray->notification_title != 0 && strlen(tray->notification_title) > 0) { strncpy(nid.szInfoTitle, tray->notification_title, sizeof(nid.szInfoTitle)); nid.uFlags |= NIF_INFO; - } else if((nid.uFlags & NIF_INFO) == NIF_INFO) { + } + else if ((nid.uFlags & NIF_INFO) == NIF_INFO) { strncpy(nid.szInfoTitle, "", sizeof(nid.szInfoTitle)); } - if(can_show_notifications == 1 && tray->notification_text != 0 && strlen(tray->notification_text) > 0){ + if (can_show_notifications == 1 && tray->notification_text != 0 && strlen(tray->notification_text) > 0) { strncpy(nid.szInfo, tray->notification_text, sizeof(nid.szInfo)); - } else if((nid.uFlags & NIF_INFO) == NIF_INFO) { + } + else if ((nid.uFlags & NIF_INFO) == NIF_INFO) { strncpy(nid.szInfo, "", sizeof(nid.szInfo)); } - if(can_show_notifications == 1 && tray->notification_cb != NULL){ + if (can_show_notifications == 1 && tray->notification_cb != NULL) { notification_cb = tray->notification_cb; } @@ -304,7 +319,8 @@ void tray_update(struct tray *tray) { } } -void tray_exit(void) { +void +tray_exit(void) { Shell_NotifyIcon(NIM_DELETE, &nid); _destroy_icon_cache(); if (hmenu != 0) {