diff --git a/CMakeLists.txt b/CMakeLists.txt index abcf5ab0..6a1e5001 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() @@ -48,7 +49,8 @@ else() target_compile_options(tray PRIVATE ${APPINDICATOR_CFLAGS}) 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 00000000..e76b199b --- /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.h b/tray.h index 26d1426f..e735ecb1 100644 --- a/tray.h +++ b/tray.h @@ -11,6 +11,10 @@ struct tray_menu; struct tray { const char *icon; char *tooltip; + const char *notification_icon; + const char *notification_text; + const char *notification_title; + void (*notification_cb)(); struct tray_menu *menu; }; diff --git a/tray_linux.c b/tray_linux.c index 1ed67169..6a7da881 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,7 +67,25 @@ 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 && notify_is_initted()) { + if(currentNotification != NULL){ + 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); + } + 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; +} diff --git a/tray_windows.c b/tray_windows.c index c2d078da..1914afc6 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,16 +142,44 @@ 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); + 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); } + if(nid.hBalloonIcon){ + DestroyIcon(nid.hBalloonIcon); + } nid.hIcon = icon; + 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)); 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; + } 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){ + notification_cb = tray->notification_cb; + } Shell_NotifyIcon(NIM_MODIFY, &nid); if (prevmenu != NULL) {