From 0d14da25d26d3ce24842ecf92ec49326d1141491 Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Sat, 22 Jul 2023 15:10:50 +0200 Subject: [PATCH 01/21] Started Windows Support for Balloon Messages --- tray.h | 2 ++ tray_linux.c | 3 +++ tray_windows.c | 23 ++++++++++++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tray.h b/tray.h index 26d1426..1b3c111 100644 --- a/tray.h +++ b/tray.h @@ -11,6 +11,8 @@ struct tray_menu; struct tray { const char *icon; char *tooltip; + char *notification_text; + char *notification_title; struct tray_menu *menu; }; diff --git a/tray_linux.c b/tray_linux.c index 4f6b029..aee1a48 100644 --- a/tray_linux.c +++ b/tray_linux.c @@ -62,6 +62,9 @@ void tray_update(struct tray *tray) { // 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){ + + } } void tray_exit(void) { loop_result = -1; } diff --git a/tray_windows.c b/tray_windows.c index c2d078d..74aa37f 100644 --- a/tray_windows.c +++ b/tray_windows.c @@ -139,16 +139,37 @@ void tray_update(struct tray *tray) { UINT id = ID_TRAY_FIRST; hmenu = _tray_menu(tray->menu, &id); SendMessage(hwnd, WM_INITMENUPOPUP, (WPARAM)hmenu, 0); - HICON icon; + HICON icon,largeIcon; ExtractIconEx(tray->icon, 0, NULL, &icon, 1); + ExtractIconEx(tray->icon, 0, &largeIcon, NULL, 1); if (nid.hIcon) { DestroyIcon(nid.hIcon); } + if(nid.hBalloonIcon){ + DestroyIcon(nid.hBalloonIcon); + } nid.hIcon = icon; + if(largeIcon){ + nid.hBalloonIcon = largeIcon; + } if(tray->tooltip != 0 && strlen(tray->tooltip) > 0) { strncpy(nid.szTip, tray->tooltip, sizeof(nid.szTip)); nid.uFlags |= NIF_TIP; } + if(tray->notification_title != 0 && strlen(tray->notification_title) > 0){ + strncpy(nid.szInfoTitle, tray->notification_title, sizeof(nid.szInfoTitle)); + nid.uFlags = NIF_INFO; + if(largeIcon){ + nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; + } + } else if((nid.uFlags & NIF_INFO) == NIF_INFO) { + strncpy(nid.szInfoTitle, "", sizeof(nid.szInfoTitle)); + } + if(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) { + strncpy(nid.szInfo, "", sizeof(nid.szInfo)); + } Shell_NotifyIcon(NIM_MODIFY, &nid); if (prevmenu != NULL) { From f8179dd9e091029bfce843f03f9be3d33ef5a95d Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Sun, 23 Jul 2023 12:22:25 +0000 Subject: [PATCH 02/21] Linux Notification support --- CMakeLists.txt | 6 +++-- cmake/FindLIBNOTIFY.cmake | 55 +++++++++++++++++++++++++++++++++++++++ tray_linux.c | 53 +++++++++++++++++++++++-------------- 3 files changed, 93 insertions(+), 21 deletions(-) create mode 100644 cmake/FindLIBNOTIFY.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index abcf5ab..9bccb8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ else() list(APPEND SRCS ${CMAKE_CURRENT_SOURCE_DIR}/tray_darwin.m) else() find_package(APPINDICATOR REQUIRED) + find_package(LIBNOTIFY REQUIRED) list(APPEND SRCS ${CMAKE_CURRENT_SOURCE_DIR}/tray_linux.c) endif() endif() @@ -46,9 +47,10 @@ else() target_link_libraries(tray PRIVATE ${COCOA}) else() target_compile_options(tray PRIVATE ${APPINDICATOR_CFLAGS}) - target_link_directories(tray PRIVATE ${APPINDICATOR_LIBRARY_DIRS}) + target_link_directories(tray PRIVATE ${APPINDICATOR_LIBRARY_DIRS}}) target_compile_definitions(tray PRIVATE TRAY_APPINDICATOR=1) - target_link_libraries(tray PRIVATE ${APPINDICATOR_LIBRARIES}) + target_compile_definitions(tray PRIVATE TRAY_LIBNOTIFY=1) + target_link_libraries(tray PRIVATE ${APPINDICATOR_LIBRARIES} ${LIBNOTIFY_LIBRARIES}) endif() endif() endif() diff --git a/cmake/FindLIBNOTIFY.cmake b/cmake/FindLIBNOTIFY.cmake new file mode 100644 index 0000000..e76b199 --- /dev/null +++ b/cmake/FindLIBNOTIFY.cmake @@ -0,0 +1,55 @@ +# - Try to find LibNotify +# This module defines the following variables: +# +# LIBNOTIFY_FOUND - LibNotify was found +# LIBNOTIFY_INCLUDE_DIRS - the LibNotify include directories +# LIBNOTIFY_LIBRARIES - link these to use LibNotify +# +# Copyright (C) 2012 Raphael Kubo da Costa +# Copyright (C) 2014 Collabora Ltd. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +find_package(PkgConfig) +pkg_check_modules(LIBNOTIFY QUIET libnotify) + +find_path(LIBNOTIFY_INCLUDE_DIRS + NAMES notify.h + HINTS ${LIBNOTIFY_INCLUDEDIR} + ${LIBNOTIFY_INCLUDE_DIRS} + PATH_SUFFIXES libnotify +) + +find_library(LIBNOTIFY_LIBRARIES + NAMES notify + HINTS ${LIBNOTIFY_LIBDIR} + ${LIBNOTIFY_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibNotify REQUIRED_VARS LIBNOTIFY_INCLUDE_DIRS LIBNOTIFY_LIBRARIES + VERSION_VAR LIBNOTIFY_VERSION) + +mark_as_advanced( + LIBNOTIFY_INCLUDE_DIRS + LIBNOTIFY_LIBRARIES +) diff --git a/tray_linux.c b/tray_linux.c index 21db4f4..b09836e 100644 --- a/tray_linux.c +++ b/tray_linux.c @@ -1,13 +1,13 @@ #include "tray.h" -#include -#include #include - +#include +#include +#include #define TRAY_APPINDICATOR_ID "tray-id" 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; @@ -16,23 +16,26 @@ static void _tray_menu_cb(GtkMenuItem *item, gpointer data) { static GtkMenuShell *_tray_menu(struct tray_menu *m) { GtkMenuShell *menu = (GtkMenuShell *)gtk_menu_new(); - for (; m != NULL && m->text != NULL; m++) { + 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) { + } + else { + 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) { + GTK_WIDGET(_tray_menu(m->submenu))); + } + 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); - } else { + } + else { 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); } } @@ -43,12 +46,13 @@ static GtkMenuShell *_tray_menu(struct tray_menu *m) { } int tray_init(struct tray *tray) { - if (gtk_init_check(0, NULL) == FALSE) { + if(gtk_init_check(0, NULL) == FALSE) { return -1; } indicator = app_indicator_new(TRAY_APPINDICATOR_ID, tray->icon, - APP_INDICATOR_CATEGORY_APPLICATION_STATUS); + APP_INDICATOR_CATEGORY_APPLICATION_STATUS); app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE); + notify_init("tray-icon"); tray_update(tray); return 0; } @@ -63,10 +67,21 @@ void tray_update(struct tray *tray) { // 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){ - + if(tray->notification_text != 0 && strlen(tray->notification_text) > 0 && notify_is_initted()) { + if(currentNotification != NULL){ + notify_notification_close(currentNotification,NULL); + g_object_unref(G_OBJECT(currentNotification)); + } + currentNotification = notify_notification_new(tray->notification_title, tray->notification_text, tray->icon); + notify_notification_show(currentNotification, NULL); } } -void tray_exit(void) { loop_result = -1; } - +void tray_exit(void) { + if(currentNotification != NULL){ + int v = notify_notification_close(currentNotification,NULL); + if(v == TRUE)g_object_unref(G_OBJECT(currentNotification)); + } + notify_uninit(); + loop_result = -1; +} From d6f00f4fbe859d5f58a68a9af41d540dd84e7323 Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Sun, 23 Jul 2023 19:32:40 +0200 Subject: [PATCH 03/21] Balloon Callback --- tray.h | 6 ++++-- tray_linux.c | 3 ++- tray_windows.c | 9 ++++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tray.h b/tray.h index 1b3c111..8641050 100644 --- a/tray.h +++ b/tray.h @@ -11,8 +11,10 @@ struct tray_menu; struct tray { const char *icon; char *tooltip; - char *notification_text; - char *notification_title; + const char *notification_text; + const char *notification_title; + const char *notification_icon; + void (*notification_cb)(); struct tray_menu *menu; }; diff --git a/tray_linux.c b/tray_linux.c index b09836e..6d169d2 100644 --- a/tray_linux.c +++ b/tray_linux.c @@ -72,7 +72,8 @@ void tray_update(struct tray *tray) { notify_notification_close(currentNotification,NULL); g_object_unref(G_OBJECT(currentNotification)); } - currentNotification = notify_notification_new(tray->notification_title, tray->notification_text, tray->icon); + 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); notify_notification_show(currentNotification, NULL); } } diff --git a/tray_windows.c b/tray_windows.c index 74aa37f..bd4c934 100644 --- a/tray_windows.c +++ b/tray_windows.c @@ -10,6 +10,7 @@ static WNDCLASSEX wc; static NOTIFYICONDATA nid; static HWND hwnd; static HMENU hmenu = NULL; +static void (*notification_cb)() = 0; static UINT wm_taskbarcreated; static LRESULT CALLBACK _tray_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, @@ -31,6 +32,8 @@ static LRESULT CALLBACK _tray_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, 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: @@ -139,9 +142,10 @@ void tray_update(struct tray *tray) { UINT id = ID_TRAY_FIRST; hmenu = _tray_menu(tray->menu, &id); SendMessage(hwnd, WM_INITMENUPOPUP, (WPARAM)hmenu, 0); + const char *notification_icon = tray->notification_icon != NULL ? tray->notification_icon : tray->icon; HICON icon,largeIcon; ExtractIconEx(tray->icon, 0, NULL, &icon, 1); - ExtractIconEx(tray->icon, 0, &largeIcon, NULL, 1); + ExtractIconEx(notification_icon, 0, &largeIcon, NULL, 1); if (nid.hIcon) { DestroyIcon(nid.hIcon); } @@ -170,6 +174,9 @@ void tray_update(struct tray *tray) { } else if((nid.uFlags & NIF_INFO) == NIF_INFO) { strncpy(nid.szInfo, "", sizeof(nid.szInfo)); } + if(tray->notification_cb != NULL){ + notification_cb = tray->notification_cb; + } Shell_NotifyIcon(NIM_MODIFY, &nid); if (prevmenu != NULL) { From 6c8600a902e0590df1651d079f6736d7d3102ac6 Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Sun, 13 Aug 2023 23:14:45 +0200 Subject: [PATCH 04/21] Windows fixes --- tray.h | 2 +- tray_windows.c | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/tray.h b/tray.h index 8641050..e735ecb 100644 --- a/tray.h +++ b/tray.h @@ -11,9 +11,9 @@ struct tray_menu; struct tray { const char *icon; char *tooltip; + const char *notification_icon; const char *notification_text; const char *notification_title; - const char *notification_icon; void (*notification_cb)(); struct tray_menu *menu; }; diff --git a/tray_windows.c b/tray_windows.c index bd4c934..1914afc 100644 --- a/tray_windows.c +++ b/tray_windows.c @@ -142,10 +142,13 @@ void tray_update(struct tray *tray) { UINT id = ID_TRAY_FIRST; hmenu = _tray_menu(tray->menu, &id); SendMessage(hwnd, WM_INITMENUPOPUP, (WPARAM)hmenu, 0); - const char *notification_icon = tray->notification_icon != NULL ? tray->notification_icon : tray->icon; HICON icon,largeIcon; ExtractIconEx(tray->icon, 0, NULL, &icon, 1); - ExtractIconEx(notification_icon, 0, &largeIcon, NULL, 1); + if(tray->notification_icon != 0){ + ExtractIconEx(tray->notification_icon, 0, &largeIcon, NULL, 1); + } else { + ExtractIconEx(tray->icon, 0, &largeIcon, NULL, 1); + } if (nid.hIcon) { DestroyIcon(nid.hIcon); } @@ -153,8 +156,9 @@ void tray_update(struct tray *tray) { DestroyIcon(nid.hBalloonIcon); } nid.hIcon = icon; - if(largeIcon){ + if(largeIcon != 0){ nid.hBalloonIcon = largeIcon; + nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; } if(tray->tooltip != 0 && strlen(tray->tooltip) > 0) { strncpy(nid.szTip, tray->tooltip, sizeof(nid.szTip)); @@ -163,15 +167,14 @@ void tray_update(struct tray *tray) { if(tray->notification_title != 0 && strlen(tray->notification_title) > 0){ strncpy(nid.szInfoTitle, tray->notification_title, sizeof(nid.szInfoTitle)); nid.uFlags = NIF_INFO; - if(largeIcon){ - nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; - } } else if((nid.uFlags & NIF_INFO) == NIF_INFO) { + nid.uFlags ^= ~NIF_INFO; strncpy(nid.szInfoTitle, "", sizeof(nid.szInfoTitle)); } if(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) { + nid.uFlags ^= ~NIF_INFO; strncpy(nid.szInfo, "", sizeof(nid.szInfo)); } if(tray->notification_cb != NULL){ From 53db1338ae3859afb439051031955f90bdec31fe Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Sun, 13 Aug 2023 23:14:51 +0200 Subject: [PATCH 05/21] Balloon Callback on Linux --- tray_linux.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tray_linux.c b/tray_linux.c index 6d169d2..6a7da88 100644 --- a/tray_linux.c +++ b/tray_linux.c @@ -74,6 +74,9 @@ void tray_update(struct tray *tray) { } 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(tray->notification_cb != NULL){ + notify_notification_add_action(currentNotification,"default","Default",tray->notification_cb,NULL,NULL); + } notify_notification_show(currentNotification, NULL); } } From 2594de996e1c703b30f0616a437e79389a35ac79 Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Sat, 19 Aug 2023 10:02:14 +0200 Subject: [PATCH 06/21] Update CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bccb8d..6a1e500 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ else() target_link_libraries(tray PRIVATE ${COCOA}) else() target_compile_options(tray PRIVATE ${APPINDICATOR_CFLAGS}) - target_link_directories(tray PRIVATE ${APPINDICATOR_LIBRARY_DIRS}}) + target_link_directories(tray PRIVATE ${APPINDICATOR_LIBRARY_DIRS}) target_compile_definitions(tray PRIVATE TRAY_APPINDICATOR=1) target_compile_definitions(tray PRIVATE TRAY_LIBNOTIFY=1) target_link_libraries(tray PRIVATE ${APPINDICATOR_LIBRARIES} ${LIBNOTIFY_LIBRARIES}) From a914b85cbe657b246c4e1ada1b90e48dbb91d5a5 Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Sun, 20 Aug 2023 15:19:55 +0200 Subject: [PATCH 07/21] Fix icon flag update --- tray_windows.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tray_windows.c b/tray_windows.c index 1914afc..d57ddcc 100644 --- a/tray_windows.c +++ b/tray_windows.c @@ -166,15 +166,15 @@ void tray_update(struct tray *tray) { } if(tray->notification_title != 0 && strlen(tray->notification_title) > 0){ strncpy(nid.szInfoTitle, tray->notification_title, sizeof(nid.szInfoTitle)); - nid.uFlags = NIF_INFO; + nid.uFlags |= NIF_INFO; } else if((nid.uFlags & NIF_INFO) == NIF_INFO) { - nid.uFlags ^= ~NIF_INFO; + nid.uFlags &= ~NIF_INFO; strncpy(nid.szInfoTitle, "", sizeof(nid.szInfoTitle)); } if(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) { - nid.uFlags ^= ~NIF_INFO; + nid.uFlags &= ~NIF_INFO; strncpy(nid.szInfo, "", sizeof(nid.szInfo)); } if(tray->notification_cb != NULL){ From 945b7fac1bc68d9fad9ff9c8af294f14b41b662a Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Sun, 20 Aug 2023 19:16:12 +0200 Subject: [PATCH 08/21] Allow balloons to be dismissed automatically --- tray_windows.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tray_windows.c b/tray_windows.c index d57ddcc..20dbedf 100644 --- a/tray_windows.c +++ b/tray_windows.c @@ -168,13 +168,11 @@ void tray_update(struct tray *tray) { strncpy(nid.szInfoTitle, tray->notification_title, sizeof(nid.szInfoTitle)); nid.uFlags |= NIF_INFO; } else if((nid.uFlags & NIF_INFO) == NIF_INFO) { - nid.uFlags &= ~NIF_INFO; strncpy(nid.szInfoTitle, "", sizeof(nid.szInfoTitle)); } if(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) { - nid.uFlags &= ~NIF_INFO; strncpy(nid.szInfo, "", sizeof(nid.szInfo)); } if(tray->notification_cb != NULL){ From 8d4192d8db55fdf0c25c1e7397406d565c31019e Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Mon, 21 Aug 2023 21:37:50 +0200 Subject: [PATCH 09/21] (windows) check notification area availablity before balloon --- tray_windows.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tray_windows.c b/tray_windows.c index 20dbedf..60df2a6 100644 --- a/tray_windows.c +++ b/tray_windows.c @@ -164,18 +164,21 @@ void tray_update(struct tray *tray) { strncpy(nid.szTip, tray->tooltip, sizeof(nid.szTip)); nid.uFlags |= NIF_TIP; } - if(tray->notification_title != 0 && strlen(tray->notification_title) > 0){ + 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){ strncpy(nid.szInfoTitle, tray->notification_title, sizeof(nid.szInfoTitle)); nid.uFlags |= NIF_INFO; } else if((nid.uFlags & NIF_INFO) == NIF_INFO) { strncpy(nid.szInfoTitle, "", sizeof(nid.szInfoTitle)); } - if(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) { strncpy(nid.szInfo, "", sizeof(nid.szInfo)); } - if(tray->notification_cb != NULL){ + if(can_show_notifications == 1 && tray->notification_cb != NULL){ notification_cb = tray->notification_cb; } Shell_NotifyIcon(NIM_MODIFY, &nid); From 6bb242d59694c657ab583fe62c3b7720a118e882 Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Sun, 17 Sep 2023 19:55:03 +0000 Subject: [PATCH 10/21] Fix for legacy appindicator --- CMakeLists.txt | 6 ++++++ cmake/FindAPPINDICATOR.cmake | 7 ++++++- tray_linux.c | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index abcf5ab..2b0f118 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,12 @@ else() target_compile_options(tray PRIVATE ${APPINDICATOR_CFLAGS}) target_link_directories(tray PRIVATE ${APPINDICATOR_LIBRARY_DIRS}) target_compile_definitions(tray PRIVATE TRAY_APPINDICATOR=1) + if(APPINDICATOR_AYATANA) + target_compile_definitions(tray PRIVATE TRAY_AYATANA_APPINDICATOR=1) + endif() + if(APPINDICATOR_LEGACY) + target_compile_definitions(tray PRIVATE TRAY_LEGACY_APPINDICATOR=1) + endif() target_link_libraries(tray PRIVATE ${APPINDICATOR_LIBRARIES}) endif() endif() diff --git a/cmake/FindAPPINDICATOR.cmake b/cmake/FindAPPINDICATOR.cmake index 5d9f718..6bfeb66 100644 --- a/cmake/FindAPPINDICATOR.cmake +++ b/cmake/FindAPPINDICATOR.cmake @@ -23,7 +23,12 @@ include(FindPackageHandleStandardArgs) PKG_CHECK_MODULES(APPINDICATOR ayatana-appindicator3-0.1) IF( APPINDICATOR_FOUND ) - SET(HAVE_AYATANAAPPINDICATOR 1) + SET(APPINDICATOR_AYATANA 1) +ELSE() + PKG_CHECK_MODULES(APPINDICATOR appindicator3-0.1) + IF( APPINDICATOR_FOUND ) + SET(APPINDICATOR_LEGACY 1) + ENDIF() ENDIF() mark_as_advanced(APPINDICATOR_INCLUDE_DIR APPINDICATOR_LIBRARY) diff --git a/tray_linux.c b/tray_linux.c index 1ed6716..d2e9454 100644 --- a/tray_linux.c +++ b/tray_linux.c @@ -1,7 +1,11 @@ #include "tray.h" #include #include +#ifdef TRAY_AYATANA_APPINDICATOR #include +#elif TRAY_LEGACY_APPINDICATOR +#include +#endif #define TRAY_APPINDICATOR_ID "tray-id" From 8da44ff3e7adf663b37cba14555216bd36a62a15 Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Mon, 18 Sep 2023 19:06:24 +0000 Subject: [PATCH 11/21] Fix fedora compile --- tray_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tray_linux.c b/tray_linux.c index b3d9f08..b546965 100644 --- a/tray_linux.c +++ b/tray_linux.c @@ -4,7 +4,7 @@ #ifdef TRAY_AYATANA_APPINDICATOR #include #elif TRAY_LEGACY_APPINDICATOR -#include +#include #endif #include From 2664388b0ed88234674a37f8cd569747522247b4 Mon Sep 17 00:00:00 2001 From: ns6089 <61738816+ns6089@users.noreply.github.com> Date: Thu, 12 Oct 2023 21:14:32 +0300 Subject: [PATCH 12/21] Use higher res image for baloon icon (#5) --- tray_windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tray_windows.c b/tray_windows.c index 60df2a6..0fcfa80 100644 --- a/tray_windows.c +++ b/tray_windows.c @@ -145,7 +145,7 @@ void tray_update(struct tray *tray) { HICON icon,largeIcon; ExtractIconEx(tray->icon, 0, NULL, &icon, 1); if(tray->notification_icon != 0){ - ExtractIconEx(tray->notification_icon, 0, &largeIcon, NULL, 1); + largeIcon = LoadImageA(NULL, tray->notification_icon, IMAGE_ICON, GetSystemMetrics(SM_CXICON) * 2, GetSystemMetrics(SM_CYICON) * 2, LR_LOADFROMFILE); } else { ExtractIconEx(tray->icon, 0, &largeIcon, NULL, 1); } From b7a02efb851fcaabdba0cc26552d2de6b9d37bde Mon Sep 17 00:00:00 2001 From: TheElixZammuto Date: Sun, 29 Oct 2023 17:27:34 +0000 Subject: [PATCH 13/21] (fix,linux) Check if the appindicator does actually work before updating anything --- tray_linux.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tray_linux.c b/tray_linux.c index b546965..03bb7ed 100644 --- a/tray_linux.c +++ b/tray_linux.c @@ -54,10 +54,11 @@ 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; app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE); - notify_init("tray-icon"); tray_update(tray); return 0; } @@ -68,26 +69,30 @@ int tray_loop(int blocking) { } void tray_update(struct tray *tray) { - app_indicator_set_icon(indicator, 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(indicator != NULL && IS_APP_INDICATOR(indicator)){ + app_indicator_set_icon(indicator, 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){ + 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(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); } - notify_notification_show(currentNotification, NULL); } } void tray_exit(void) { - if(currentNotification != NULL){ + if(currentNotification != NULL && NOTIFY_IS_NOTIFICATION(currentNotification)){ int v = notify_notification_close(currentNotification,NULL); if(v == TRUE)g_object_unref(G_OBJECT(currentNotification)); } From 5da8e87100c1c99a39cf04c5db21d1fb23279b6c Mon Sep 17 00:00:00 2001 From: TheElixZammuto Date: Mon, 30 Oct 2023 19:30:53 +0000 Subject: [PATCH 14/21] Fix Ayatana Build --- tray_linux.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tray_linux.c b/tray_linux.c index 03bb7ed..a3393e8 100644 --- a/tray_linux.c +++ b/tray_linux.c @@ -3,6 +3,8 @@ #include #ifdef TRAY_AYATANA_APPINDICATOR #include +/*Ayatana flavor of appindicator has a different name for the same Macro*/ +#define IS_APP_INDICATOR APP_IS_INDICATOR #elif TRAY_LEGACY_APPINDICATOR #include #endif From e97c1392d4ff539dd4eb5f012e7fdddc35f3fa88 Mon Sep 17 00:00:00 2001 From: TheElixZammuto Date: Mon, 30 Oct 2023 19:58:32 +0000 Subject: [PATCH 15/21] Use ifndef instead of relying of the defined header --- tray_linux.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tray_linux.c b/tray_linux.c index a3393e8..366a4b9 100644 --- a/tray_linux.c +++ b/tray_linux.c @@ -3,11 +3,12 @@ #include #ifdef TRAY_AYATANA_APPINDICATOR #include -/*Ayatana flavor of appindicator has a different name for the same Macro*/ -#define IS_APP_INDICATOR APP_IS_INDICATOR #elif TRAY_LEGACY_APPINDICATOR #include #endif +#ifndef IS_APP_INDICATOR +#define IS_APP_INDICATOR APP_IS_INDICATOR +#endif #include #define TRAY_APPINDICATOR_ID "tray-id" From 8bb9978991a1438fe0665513012628d85f0783ce Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 31 Dec 2023 14:33:32 -0600 Subject: [PATCH 16/21] Constify tooltip string pointer (#8) --- tray.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tray.h b/tray.h index e735ecb..228e9c0 100644 --- a/tray.h +++ b/tray.h @@ -10,7 +10,7 @@ struct tray_menu; struct tray { const char *icon; - char *tooltip; + const char *tooltip; const char *notification_icon; const char *notification_text; const char *notification_title; From 2bf1c610300b27f8d8ce87e2f13223fc83efeb42 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 10 Jan 2024 18:46:32 -0800 Subject: [PATCH 17/21] Perform all GTK interactions on the tray loop thread (#9) --- tray_linux.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/tray_linux.c b/tray_linux.c index 366a4b9..fc643c9 100644 --- a/tray_linux.c +++ b/tray_linux.c @@ -1,6 +1,7 @@ #include "tray.h" #include #include +#include #ifdef TRAY_AYATANA_APPINDICATOR #include #elif TRAY_LEGACY_APPINDICATOR @@ -13,6 +14,10 @@ #include #define TRAY_APPINDICATOR_ID "tray-id" +static bool async_update_pending = false; +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 NotifyNotification *currentNotification = NULL; @@ -71,7 +76,9 @@ int tray_loop(int blocking) { return loop_result; } -void tray_update(struct tray *tray) { +static gboolean tray_update_internal(gpointer user_data) { + struct tray *tray = user_data; + if(indicator != NULL && IS_APP_INDICATOR(indicator)){ app_indicator_set_icon(indicator, tray->icon); // GTK is all about reference counting, so previous menu should be destroyed @@ -92,13 +99,64 @@ void tray_update(struct tray *tray) { notify_notification_show(currentNotification, NULL); } } + + // Unwait any pending tray_update() calls + pthread_mutex_lock(&async_update_mutex); + async_update_pending = false; + pthread_cond_broadcast(&async_update_cv); + pthread_mutex_unlock(&async_update_mutex); + return G_SOURCE_REMOVE; +} + +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. + + if (g_main_context_is_owner(g_main_context_default())) { + // Invoke the callback directly if we're on the loop thread + tray_update_internal(tray); + } + else { + // If there's already an update pending, wait for it to complete + // and claim the next pending update slot. + pthread_mutex_lock(&async_update_mutex); + while (async_update_pending) { + pthread_cond_wait(&async_update_cv, &async_update_mutex); + } + async_update_pending = true; + pthread_mutex_unlock(&async_update_mutex); + + // Queue the update callback to the tray thread + g_main_context_invoke(NULL, tray_update_internal, tray); + + // Wait for the callback to run + pthread_mutex_lock(&async_update_mutex); + while (async_update_pending) { + pthread_cond_wait(&async_update_cv, &async_update_mutex); + } + pthread_mutex_unlock(&async_update_mutex); + } } -void tray_exit(void) { +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(); - loop_result = -1; + return G_SOURCE_REMOVE; +} + +void tray_exit(void) { + // Wait for any pending callbacks to complete + pthread_mutex_lock(&async_update_mutex); + while (async_update_pending) { + pthread_cond_wait(&async_update_cv, &async_update_mutex); + } + pthread_mutex_unlock(&async_update_mutex); + + // Perform cleanup on the main thread + loop_result = -1; + g_main_context_invoke(NULL, tray_exit_internal, NULL); } From d649813cc7ff87ccd359df9ebced674e0bec4d15 Mon Sep 17 00:00:00 2001 From: brycerocky <56776312+brycerocky@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:48:34 -0800 Subject: [PATCH 18/21] Allow tray icons to be precached. On Windows, loading icons through the shell API can trigger DRM issues, leading to windows becoming invisible. This changelist works around these issues by preloading icons at initialization and avoiding calling the shell API methods on demand. --- tray.h | 2 + tray_windows.c | 109 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 94 insertions(+), 17 deletions(-) diff --git a/tray.h b/tray.h index 228e9c0..68b5324 100644 --- a/tray.h +++ b/tray.h @@ -16,6 +16,8 @@ struct tray { const char *notification_title; void (*notification_cb)(); struct tray_menu *menu; + const int iconPathCount; + const char *allIconPaths[]; }; struct tray_menu { diff --git a/tray_windows.c b/tray_windows.c index 0fcfa80..7f43bab 100644 --- a/tray_windows.c +++ b/tray_windows.c @@ -6,6 +6,19 @@ #define WC_TRAY_CLASS_NAME "TRAY" #define ID_TRAY_FIRST 1000 +struct icon_info { + const char *path; + HICON icon; + HICON large_icon; + HICON notification_icon; +}; + +enum IconType { + REGULAR = 1, + LARGE, + NOTIFICATION +}; + static WNDCLASSEX wc; static NOTIFYICONDATA nid; static HWND hwnd; @@ -13,6 +26,9 @@ static HMENU hmenu = NULL; static void (*notification_cb)() = 0; 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) { switch (msg) { @@ -92,9 +108,73 @@ static HMENU _tray_menu(struct tray_menu *m, UINT *id) { return hmenu; } +struct icon_info _create_icon_info(const char * path) { + struct icon_info info; + info.path = path; + ExtractIconEx(path, 0, &info.large_icon, &info.icon, 1); + info.notification_icon = LoadImageA(NULL, path, IMAGE_ICON, GetSystemMetrics(SM_CXICON) * 2, GetSystemMetrics(SM_CYICON) * 2, LR_LOADFROMFILE); + return info; +} + +void _init_icon_cache(const char ** paths, int count) { + icon_info_count = count; + icon_infos = malloc(sizeof(struct icon_info) * icon_info_count); + + for (int i = 0; i < count; ++i) { + icon_infos[i] = _create_icon_info(paths[i]); + } +} + +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(icon_infos); + icon_infos = NULL; + icon_info_count = 0; +} + +HICON _fetch_cached_icon(const char * path, enum IconType icon_type) { + for (int i = 0; i < icon_info_count; ++i) { + if (strcmp(icon_infos[i].path, path) == 0) { + switch (icon_type) { + case REGULAR: + return icon_infos[i].icon; + case LARGE: + return icon_infos[i].large_icon; + case NOTIFICATION: + return icon_infos[i].notification_icon; + } + } + } + + return NULL; +} + +HICON _fetch_icon(const char * path, enum IconType icon_type) { + HICON value = _fetch_cached_icon(path, icon_type); + + if (value != NULL) { + return value; + } + + // Expand cache, fetch, and retry + icon_info_count += 1; + icon_infos = realloc(icon_infos, sizeof(struct icon_info) * icon_info_count); + int index = icon_info_count - 1; + icon_infos[icon_info_count - 1] = _create_icon_info(path); + + return _fetch_icon(path, icon_type); +} + int tray_init(struct tray *tray) { wm_taskbarcreated = RegisterWindowMessage("TaskbarCreated"); + _init_icon_cache(tray->allIconPaths, tray->iconPathCount); + memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(WNDCLASSEX); wc.lpfnWndProc = _tray_wnd_proc; @@ -138,24 +218,20 @@ int tray_loop(int blocking) { } void tray_update(struct tray *tray) { - HMENU prevmenu = hmenu; UINT id = ID_TRAY_FIRST; + HMENU prevmenu = hmenu; hmenu = _tray_menu(tray->menu, &id); SendMessage(hwnd, WM_INITMENUPOPUP, (WPARAM)hmenu, 0); - HICON icon,largeIcon; - ExtractIconEx(tray->icon, 0, NULL, &icon, 1); - if(tray->notification_icon != 0){ - largeIcon = LoadImageA(NULL, tray->notification_icon, IMAGE_ICON, GetSystemMetrics(SM_CXICON) * 2, GetSystemMetrics(SM_CYICON) * 2, LR_LOADFROMFILE); - } else { - ExtractIconEx(tray->icon, 0, &largeIcon, NULL, 1); - } - if (nid.hIcon) { - DestroyIcon(nid.hIcon); - } - if(nid.hBalloonIcon){ - DestroyIcon(nid.hBalloonIcon); + + HICON icon = _fetch_icon(tray->icon, REGULAR); + HICON largeIcon = tray->notification_icon != 0 + ? _fetch_icon(tray->notification_icon, NOTIFICATION) + : _fetch_icon(tray->icon, LARGE); + + if (icon != NULL) { + nid.hIcon = icon; } - nid.hIcon = icon; + if(largeIcon != 0){ nid.hBalloonIcon = largeIcon; nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; @@ -181,6 +257,7 @@ void tray_update(struct tray *tray) { if(can_show_notifications == 1 && tray->notification_cb != NULL){ notification_cb = tray->notification_cb; } + Shell_NotifyIcon(NIM_MODIFY, &nid); if (prevmenu != NULL) { @@ -190,9 +267,7 @@ void tray_update(struct tray *tray) { void tray_exit(void) { Shell_NotifyIcon(NIM_DELETE, &nid); - if (nid.hIcon != 0) { - DestroyIcon(nid.hIcon); - } + _destroy_icon_cache(); if (hmenu != 0) { DestroyMenu(hmenu); } From 32ca496f5ea5e9f0f46b692e973d98f16130be5a Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 14 Mar 2024 17:41:09 -0500 Subject: [PATCH 19/21] Avoid possible UAF if an icon path is dynamically allocated memory --- tray_windows.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tray_windows.c b/tray_windows.c index 7f43bab..21c1d70 100644 --- a/tray_windows.c +++ b/tray_windows.c @@ -110,7 +110,7 @@ static HMENU _tray_menu(struct tray_menu *m, UINT *id) { struct icon_info _create_icon_info(const char * path) { struct icon_info info; - info.path = path; + info.path = strdup(path); ExtractIconEx(path, 0, &info.large_icon, &info.icon, 1); info.notification_icon = LoadImageA(NULL, path, IMAGE_ICON, GetSystemMetrics(SM_CXICON) * 2, GetSystemMetrics(SM_CYICON) * 2, LR_LOADFROMFILE); return info; @@ -130,6 +130,7 @@ void _destroy_icon_cache() { DestroyIcon(icon_infos[i].icon); DestroyIcon(icon_infos[i].large_icon); DestroyIcon(icon_infos[i].notification_icon); + free((void*) icon_infos[i].path); } free(icon_infos); From 8d9493784cb61a0a96c8a619bc35f9614527bce2 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 14 Mar 2024 17:41:49 -0500 Subject: [PATCH 20/21] Fix missing small icon on some systems --- tray_windows.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tray_windows.c b/tray_windows.c index 21c1d70..ed5a792 100644 --- a/tray_windows.c +++ b/tray_windows.c @@ -111,7 +111,12 @@ static HMENU _tray_menu(struct tray_menu *m, UINT *id) { struct icon_info _create_icon_info(const char * path) { struct icon_info info; info.path = strdup(path); - ExtractIconEx(path, 0, &info.large_icon, &info.icon, 1); + + // These must be separate invocations otherwise Windows may opt to only return large or small icons. + // MSDN does not explicitly state this anywhere, but it has been observed on some machines. + ExtractIconEx(path, 0, &info.large_icon, NULL, 1); + ExtractIconEx(path, 0, NULL, &info.icon, 1); + info.notification_icon = LoadImageA(NULL, path, IMAGE_ICON, GetSystemMetrics(SM_CXICON) * 2, GetSystemMetrics(SM_CYICON) * 2, LR_LOADFROMFILE); return info; } From 585d4f0d50fe8a2d9cc187e98eecc5037672e80c Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 14 Mar 2024 17:49:20 -0500 Subject: [PATCH 21/21] Avoid infinite recursion if icon load fails --- tray_windows.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/tray_windows.c b/tray_windows.c index ed5a792..7176291 100644 --- a/tray_windows.c +++ b/tray_windows.c @@ -143,28 +143,23 @@ void _destroy_icon_cache() { icon_info_count = 0; } -HICON _fetch_cached_icon(const char * path, enum IconType icon_type) { - for (int i = 0; i < icon_info_count; ++i) { - if (strcmp(icon_infos[i].path, path) == 0) { - switch (icon_type) { - case REGULAR: - return icon_infos[i].icon; - case LARGE: - return icon_infos[i].large_icon; - case NOTIFICATION: - return icon_infos[i].notification_icon; - } - } +HICON _fetch_cached_icon(struct icon_info *icon_record, enum IconType icon_type) { + switch (icon_type) { + case REGULAR: + return icon_record->icon; + case LARGE: + return icon_record->large_icon; + case NOTIFICATION: + return icon_record->notification_icon; } - - return NULL; } HICON _fetch_icon(const char * path, enum IconType icon_type) { - HICON value = _fetch_cached_icon(path, icon_type); - - if (value != NULL) { - return value; + // Find a cached icon by path + for (int i = 0; i < icon_info_count; ++i) { + if (strcmp(icon_infos[i].path, path) == 0) { + return _fetch_cached_icon(&icon_infos[i], icon_type); + } } // Expand cache, fetch, and retry @@ -173,7 +168,7 @@ HICON _fetch_icon(const char * path, enum IconType icon_type) { int index = icon_info_count - 1; icon_infos[icon_info_count - 1] = _create_icon_info(path); - return _fetch_icon(path, icon_type); + return _fetch_cached_icon(&icon_infos[icon_info_count - 1], icon_type); } int tray_init(struct tray *tray) {