├── go.mod ├── LICENSE.txt ├── example └── main.go ├── libnotify ├── notify.h ├── internal.h ├── notification.h ├── notify.c └── notification.c ├── README.md └── simple.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/codegoalie/golibnotify 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2018 Mark Bates 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/codegoalie/golibnotify" 9 | ) 10 | 11 | func main() { 12 | 13 | // Get an instance of a SimpleNotifier 14 | notifier := golibnotify.NewSimpleNotifier("my-cool-app-name") 15 | 16 | // Show a new notification 17 | err := notifier.Show("A summary", "Some body text", "a/path/to/an/icon.png") 18 | if err != nil { 19 | err = fmt.Errorf("failed to send a notification: %w", err) 20 | log.Fatal(err) 21 | } 22 | 23 | time.Sleep(3 * time.Second) 24 | 25 | // Update an existing notification (or send a new one if one hasn't been sent) 26 | err = notifier.Update("A new summary", "Some different body text", "another/path/to/icon.png") 27 | if err != nil { 28 | err = fmt.Errorf("failed to update a notification: %w", err) 29 | log.Fatal(err) 30 | } 31 | 32 | time.Sleep(3 * time.Second) 33 | 34 | // Remove an existing notification 35 | err = notifier.Close() 36 | if err != nil { 37 | err = fmt.Errorf("failed to close a notification: %w", err) 38 | log.Fatal(err) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /libnotify/notify.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- 2 | * 3 | * Copyright (C) 2004-2006 Christian Hammond 4 | * Copyright (C) 2010 Red Hat, Inc. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the 18 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 | * Boston, MA 02111-1307, USA. 20 | */ 21 | 22 | #ifndef _LIBNOTIFY_NOTIFY_H_ 23 | #define _LIBNOTIFY_NOTIFY_H_ 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | G_BEGIN_DECLS 32 | 33 | gboolean notify_init (const char *app_name); 34 | void notify_uninit (void); 35 | gboolean notify_is_initted (void); 36 | 37 | const char *notify_get_app_name (void); 38 | void notify_set_app_name (const char *app_name); 39 | 40 | GList *notify_get_server_caps (void); 41 | 42 | gboolean notify_get_server_info (char **ret_name, 43 | char **ret_vendor, 44 | char **ret_version, 45 | char **ret_spec_version); 46 | 47 | G_END_DECLS 48 | 49 | #endif /* _LIBNOTIFY_NOTIFY_H_ */ 50 | -------------------------------------------------------------------------------- /libnotify/internal.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- 2 | * 3 | * @file libnotify/internal.h Internal definitions 4 | * 5 | * @Copyright (C) 2006 Christian Hammond 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2.1 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the 19 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | #ifndef _LIBNOTIFY_INTERNAL_H_ 23 | #define _LIBNOTIFY_INTERNAL_H_ 24 | 25 | #define NOTIFY_DBUS_NAME "org.freedesktop.Notifications" 26 | #define NOTIFY_DBUS_CORE_INTERFACE "org.freedesktop.Notifications" 27 | #define NOTIFY_DBUS_CORE_OBJECT "/org/freedesktop/Notifications" 28 | 29 | G_BEGIN_DECLS 30 | 31 | GDBusProxy * _notify_get_proxy (GError **error); 32 | 33 | void _notify_cache_add_notification (NotifyNotification *n); 34 | void _notify_cache_remove_notification (NotifyNotification *n); 35 | gint _notify_notification_get_timeout (const NotifyNotification *n); 36 | gboolean _notify_notification_has_nondefault_actions (const NotifyNotification *n); 37 | gboolean _notify_check_spec_version (int major, int minor); 38 | 39 | G_END_DECLS 40 | 41 | #endif /* _LIBNOTIFY_INTERNAL_H_ */ 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # golibnotify 2 | 3 | `golibnotify` implements go bindings for 4 | [`libnotify`](https://developer.gnome.org/libnotify/unstable/) to create, send, 5 | and update OS level notifications. It does not shell out to `notify-send` so it 6 | can update existing notifications as well as create new ones. 7 | 8 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/codegoalie/golibnotify?tab=doc) 9 | [![Go Report Card](https://goreportcard.com/badge/github.com/codegoalie/golibnotify)](https://goreportcard.com/report/github.com/codegoalie/golibnotify) 10 | ![GitHub](https://img.shields.io/github/license/codegoalie/golibnotify?style=flat-square) 11 | [![Sourcegraph Badge](https://sourcegraph.com/github.com/codegoalie/golibnotify/-/badge.svg)](https://sourcegraph.com/github.com/codegoalie/golibnotify?badge) 12 | 13 | ## Installation 14 | 15 | This package requires CGO and the libnotify (`libnotify-dev`) shared library to be installed. 16 | 17 | On Ubuntu or Debian using `apt`, you can install with `sudo apt-get install libnotify-dev` 18 | 19 | ## Usage 20 | 21 | ```go 22 | // Get an instance of a SimpleNotifier 23 | notifier := golibnotify.NewSimpleNotifier("my-cool-app-name") 24 | 25 | // Show a new notification 26 | err = notifier.Show("A summary", "Some body text", "a/path/to/an/icon.png") 27 | 28 | // Update an existing notification (or send a new one if one hasn't been sent) 29 | err = notifier.Update("A new summary", "Some different body text", "another/path/to/icon.png") 30 | 31 | // Remove an existing notification 32 | err = notifier.Close() 33 | ``` 34 | 35 | See also [examples](https://github.com/codegoalie/golibnotify/examples) for a complete example 36 | 37 | ## Roadmap 38 | 39 | - [ ] Add API that returns a Notification instance to manage multiple notifications at once 40 | - [ ] Better support for notification icons (PixBuf?) 41 | - [ ] Support notification timeouts 42 | - [ ] Support notification urgencies 43 | 44 | ## Contributions 45 | 46 | Thanks for wanting to contribute! 47 | 48 | 1. Open an issue describing your problem, solution, issue, suggestion, feature, 49 | etc. to ensure it's likely to get merged. 50 | 1. Fork the project. 51 | 1. Make your changes. 52 | 1. Open a PR against `master` here. 53 | 1. Celebrate being awesome! 54 | 55 | ## Author 56 | 57 | [@codegoalie](https://codegoalie.com) 58 | -------------------------------------------------------------------------------- /simple.go: -------------------------------------------------------------------------------- 1 | // Package golibnotify implements go bindings for libnotify to create, send, and 2 | // update OS level notifications. It does not shell out to `notify-send` so it can 3 | // update existing notifications as well as create new ones. 4 | // 5 | // This package requires CGO and the libnotify (libnotify-dev) shared library 6 | // to be installed. 7 | // 8 | // On Ubuntu or Debian using apt, you can install with: 9 | // sudo apt-get install libnotify-dev 10 | package golibnotify 11 | 12 | /* 13 | #cgo pkg-config: libnotify 14 | #include "libnotify/notify.h" 15 | #include 16 | */ 17 | import "C" 18 | import ( 19 | "errors" 20 | "fmt" 21 | "runtime" 22 | ) 23 | 24 | // SimpleNotifier is an instance of an application sending notifications 25 | type SimpleNotifier struct { 26 | appName string 27 | notification notification 28 | } 29 | 30 | // notification is an instance of a particular notification 31 | type notification *C.NotifyNotification 32 | 33 | // NewSimpleNotifier initializes a new application to send notifications 34 | func NewSimpleNotifier(applicationName string) *SimpleNotifier { 35 | cAppName := C.CString(applicationName) 36 | defer C.g_free(C.gpointer(cAppName)) 37 | C.notify_init(cAppName) 38 | 39 | notifier := &SimpleNotifier{appName: applicationName} 40 | runtime.SetFinalizer(notifier, func(n *SimpleNotifier) { 41 | C.g_object_unref(C.gpointer(n.notification)) 42 | }) 43 | return notifier 44 | } 45 | 46 | // ApplicationName returns the current application's initialized name 47 | func (n *SimpleNotifier) ApplicationName() string { 48 | return n.appName 49 | } 50 | 51 | // Show creates a new notification and sends it to the OS 52 | func (n *SimpleNotifier) Show(summary, body, icon string) error { 53 | cSummary := C.CString(summary) 54 | defer C.g_free(C.gpointer(cSummary)) 55 | cBody := C.CString(body) 56 | defer C.g_free(C.gpointer(cBody)) 57 | cIcon := C.CString(icon) 58 | defer C.g_free(C.gpointer(cIcon)) 59 | 60 | n.notification = C.notify_notification_new(cSummary, cBody, cIcon) 61 | 62 | return show(n.notification) 63 | } 64 | 65 | // Update an existing notification with new information 66 | func (n *SimpleNotifier) Update(summary, body, icon string) error { 67 | if n.notification == nil { 68 | return n.Show(summary, body, icon) 69 | } 70 | 71 | cSummary := C.CString(summary) 72 | defer C.g_free(C.gpointer(cSummary)) 73 | cBody := C.CString(body) 74 | defer C.g_free(C.gpointer(cBody)) 75 | cIcon := C.CString(icon) 76 | defer C.g_free(C.gpointer(cIcon)) 77 | C.notify_notification_update(n.notification, cSummary, cBody, cIcon) 78 | 79 | return show(n.notification) 80 | } 81 | 82 | // Close removes the notification from the OS 83 | func (n *SimpleNotifier) Close() error { 84 | if n.notification == nil { 85 | return fmt.Errorf("failed to close notification: no notification exists") 86 | } 87 | 88 | return close(n.notification) 89 | } 90 | 91 | func show(notif notification) error { 92 | var cErr **C.GError 93 | C.notify_notification_show(notif, cErr) 94 | if cErr != nil { 95 | defer C.g_error_free(*cErr) 96 | return errors.New(C.GoString((*cErr).message)) 97 | } 98 | return nil 99 | } 100 | 101 | func close(notif notification) error { 102 | var cErr **C.GError 103 | C.notify_notification_close(notif, cErr) 104 | if cErr != nil { 105 | defer C.g_error_free(*cErr) 106 | return errors.New(C.GoString((*cErr).message)) 107 | } 108 | return nil 109 | } 110 | -------------------------------------------------------------------------------- /libnotify/notification.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- 2 | * 3 | * Copyright (C) 2006 Christian Hammond 4 | * Copyright (C) 2006 John Palmieri 5 | * Copyright (C) 2010 Red Hat, Inc. 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2.1 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the 19 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | #ifndef _NOTIFY_NOTIFICATION_H_ 24 | #define _NOTIFY_NOTIFICATION_H_ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | G_BEGIN_DECLS 31 | 32 | /** 33 | * NOTIFY_EXPIRES_DEFAULT: 34 | * 35 | * The default expiration time on a notification. 36 | */ 37 | #define NOTIFY_EXPIRES_DEFAULT -1 38 | 39 | /** 40 | * NOTIFY_EXPIRES_NEVER: 41 | * 42 | * The notification never expires. It stays open until closed by the calling API 43 | * or the user. 44 | */ 45 | #define NOTIFY_EXPIRES_NEVER 0 46 | 47 | #define NOTIFY_TYPE_NOTIFICATION (notify_notification_get_type ()) 48 | #define NOTIFY_NOTIFICATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NOTIFY_TYPE_NOTIFICATION, NotifyNotification)) 49 | #define NOTIFY_NOTIFICATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NOTIFY_TYPE_NOTIFICATION, NotifyNotificationClass)) 50 | #define NOTIFY_IS_NOTIFICATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NOTIFY_TYPE_NOTIFICATION)) 51 | #define NOTIFY_IS_NOTIFICATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NOTIFY_TYPE_NOTIFICATION)) 52 | #define NOTIFY_NOTIFICATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NOTIFY_TYPE_NOTIFICATION, NotifyNotificationClass)) 53 | 54 | typedef struct _NotifyNotification NotifyNotification; 55 | typedef struct _NotifyNotificationClass NotifyNotificationClass; 56 | typedef struct _NotifyNotificationPrivate NotifyNotificationPrivate; 57 | 58 | struct _NotifyNotification 59 | { 60 | /*< private >*/ 61 | GObject parent_object; 62 | 63 | NotifyNotificationPrivate *priv; 64 | }; 65 | 66 | struct _NotifyNotificationClass 67 | { 68 | GObjectClass parent_class; 69 | 70 | /* Signals */ 71 | void (*closed) (NotifyNotification *notification); 72 | }; 73 | 74 | 75 | /** 76 | * NotifyUrgency: 77 | * @NOTIFY_URGENCY_LOW: Low urgency. Used for unimportant notifications. 78 | * @NOTIFY_URGENCY_NORMAL: Normal urgency. Used for most standard notifications. 79 | * @NOTIFY_URGENCY_CRITICAL: Critical urgency. Used for very important notifications. 80 | * 81 | * The urgency level of the notification. 82 | */ 83 | typedef enum 84 | { 85 | NOTIFY_URGENCY_LOW, 86 | NOTIFY_URGENCY_NORMAL, 87 | NOTIFY_URGENCY_CRITICAL, 88 | 89 | } NotifyUrgency; 90 | 91 | /** 92 | * NotifyActionCallback: 93 | * @notification: 94 | * @action: 95 | * @user_data: 96 | * 97 | * An action callback function. 98 | */ 99 | typedef void (*NotifyActionCallback) (NotifyNotification *notification, 100 | char *action, 101 | gpointer user_data); 102 | 103 | /** 104 | * NOTIFY_ACTION_CALLBACK: 105 | * @func: The function to cast. 106 | * 107 | * A convenience macro for casting a function to a #NotifyActionCallback. This 108 | * is much like G_CALLBACK(). 109 | */ 110 | #define NOTIFY_ACTION_CALLBACK(func) ((NotifyActionCallback)(func)) 111 | 112 | GType notify_notification_get_type (void); 113 | 114 | NotifyNotification *notify_notification_new (const char *summary, 115 | const char *body, 116 | const char *icon); 117 | 118 | gboolean notify_notification_update (NotifyNotification *notification, 119 | const char *summary, 120 | const char *body, 121 | const char *icon); 122 | 123 | gboolean notify_notification_show (NotifyNotification *notification, 124 | GError **error); 125 | 126 | void notify_notification_set_timeout (NotifyNotification *notification, 127 | gint timeout); 128 | 129 | void notify_notification_set_category (NotifyNotification *notification, 130 | const char *category); 131 | 132 | void notify_notification_set_urgency (NotifyNotification *notification, 133 | NotifyUrgency urgency); 134 | 135 | void notify_notification_set_image_from_pixbuf (NotifyNotification *notification, 136 | GdkPixbuf *pixbuf); 137 | 138 | #ifndef LIBNOTIFY_DISABLE_DEPRECATED 139 | void notify_notification_set_icon_from_pixbuf (NotifyNotification *notification, 140 | GdkPixbuf *icon); 141 | 142 | void notify_notification_set_hint_int32 (NotifyNotification *notification, 143 | const char *key, 144 | gint value); 145 | void notify_notification_set_hint_uint32 (NotifyNotification *notification, 146 | const char *key, 147 | guint value); 148 | 149 | void notify_notification_set_hint_double (NotifyNotification *notification, 150 | const char *key, 151 | gdouble value); 152 | 153 | void notify_notification_set_hint_string (NotifyNotification *notification, 154 | const char *key, 155 | const char *value); 156 | 157 | void notify_notification_set_hint_byte (NotifyNotification *notification, 158 | const char *key, 159 | guchar value); 160 | 161 | void notify_notification_set_hint_byte_array (NotifyNotification *notification, 162 | const char *key, 163 | const guchar *value, 164 | gsize len); 165 | #endif 166 | 167 | void notify_notification_set_hint (NotifyNotification *notification, 168 | const char *key, 169 | GVariant *value); 170 | 171 | void notify_notification_set_app_name (NotifyNotification *notification, 172 | const char *app_name); 173 | 174 | void notify_notification_clear_hints (NotifyNotification *notification); 175 | 176 | void notify_notification_add_action (NotifyNotification *notification, 177 | const char *action, 178 | const char *label, 179 | NotifyActionCallback callback, 180 | gpointer user_data, 181 | GFreeFunc free_func); 182 | 183 | void notify_notification_clear_actions (NotifyNotification *notification); 184 | gboolean notify_notification_close (NotifyNotification *notification, 185 | GError **error); 186 | 187 | gint notify_notification_get_closed_reason (const NotifyNotification *notification); 188 | 189 | G_END_DECLS 190 | #endif /* NOTIFY_NOTIFICATION_H */ 191 | -------------------------------------------------------------------------------- /libnotify/notify.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- 2 | * 3 | * Copyright (C) 2004-2006 Christian Hammond 4 | * Copyright (C) 2004-2006 Mike Hearn 5 | * Copyright (C) 2010 Red Hat, Inc. 6 | * Copyright © 2010 Christian Persch 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the 20 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 21 | * Boston, MA 02111-1307, USA. 22 | */ 23 | 24 | #include "config.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | #include "notify.h" 35 | #include "internal.h" 36 | #include "notify-marshal.h" 37 | 38 | /** 39 | * SECTION:notify 40 | * @Short_description: Notification API 41 | * @Title: notify 42 | */ 43 | 44 | static gboolean _initted = FALSE; 45 | static char *_app_name = NULL; 46 | static GDBusProxy *_proxy = NULL; 47 | static GList *_active_notifications = NULL; 48 | static int _spec_version_major = 0; 49 | static int _spec_version_minor = 0; 50 | 51 | gboolean 52 | _notify_check_spec_version (int major, 53 | int minor) 54 | { 55 | if (_spec_version_major > major) 56 | return TRUE; 57 | if (_spec_version_major < major) 58 | return FALSE; 59 | return _spec_version_minor >= minor; 60 | } 61 | 62 | static gboolean 63 | _notify_get_server_info (char **ret_name, 64 | char **ret_vendor, 65 | char **ret_version, 66 | char **ret_spec_version, 67 | GError **error) 68 | { 69 | GDBusProxy *proxy; 70 | GVariant *result; 71 | 72 | proxy = _notify_get_proxy (error); 73 | if (proxy == NULL) { 74 | return FALSE; 75 | } 76 | 77 | result = g_dbus_proxy_call_sync (proxy, 78 | "GetServerInformation", 79 | g_variant_new ("()"), 80 | G_DBUS_CALL_FLAGS_NONE, 81 | -1 /* FIXME shorter timeout? */, 82 | NULL, 83 | error); 84 | if (result == NULL) { 85 | return FALSE; 86 | } 87 | if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(ssss)"))) { 88 | g_variant_unref (result); 89 | g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, 90 | "Unexpected reply type"); 91 | return FALSE; 92 | } 93 | 94 | g_variant_get (result, "(ssss)", 95 | ret_name, 96 | ret_vendor, 97 | ret_version, 98 | ret_spec_version); 99 | g_variant_unref (result); 100 | return TRUE; 101 | } 102 | 103 | static gboolean 104 | _notify_update_spec_version (GError **error) 105 | { 106 | char *spec_version; 107 | 108 | if (!_notify_get_server_info (NULL, NULL, NULL, &spec_version, error)) { 109 | return FALSE; 110 | } 111 | 112 | sscanf (spec_version, 113 | "%d.%d", 114 | &_spec_version_major, 115 | &_spec_version_minor); 116 | 117 | g_free (spec_version); 118 | 119 | return TRUE; 120 | } 121 | 122 | 123 | /** 124 | * notify_set_app_name: 125 | * @app_name: The name of the application 126 | * 127 | * Sets the application name. 128 | * 129 | */ 130 | void 131 | notify_set_app_name (const char *app_name) 132 | { 133 | g_free (_app_name); 134 | _app_name = g_strdup (app_name); 135 | } 136 | 137 | /** 138 | * notify_init: 139 | * @app_name: The name of the application initializing libnotify. 140 | * 141 | * Initialized libnotify. This must be called before any other functions. 142 | * 143 | * Returns: %TRUE if successful, or %FALSE on error. 144 | */ 145 | gboolean 146 | notify_init (const char *app_name) 147 | { 148 | g_return_val_if_fail (app_name != NULL, FALSE); 149 | g_return_val_if_fail (*app_name != '\0', FALSE); 150 | 151 | if (_initted) 152 | return TRUE; 153 | 154 | notify_set_app_name (app_name); 155 | 156 | #if !GLIB_CHECK_VERSION (2, 36, 0) 157 | g_type_init (); 158 | #endif 159 | 160 | _initted = TRUE; 161 | 162 | return TRUE; 163 | } 164 | 165 | /** 166 | * notify_get_app_name: 167 | * 168 | * Gets the application name registered. 169 | * 170 | * Returns: The registered application name, passed to notify_init(). 171 | */ 172 | const char * 173 | notify_get_app_name (void) 174 | { 175 | return _app_name; 176 | } 177 | 178 | /** 179 | * notify_uninit: 180 | * 181 | * Uninitialized libnotify. 182 | * 183 | * This should be called when the program no longer needs libnotify for 184 | * the rest of its lifecycle, typically just before exitting. 185 | */ 186 | void 187 | notify_uninit (void) 188 | { 189 | GList *l; 190 | 191 | if (!_initted) { 192 | return; 193 | } 194 | 195 | if (_app_name != NULL) { 196 | g_free (_app_name); 197 | _app_name = NULL; 198 | } 199 | 200 | for (l = _active_notifications; l != NULL; l = l->next) { 201 | NotifyNotification *n = NOTIFY_NOTIFICATION (l->data); 202 | 203 | if (_notify_notification_get_timeout (n) == 0 || 204 | _notify_notification_has_nondefault_actions (n)) { 205 | notify_notification_close (n, NULL); 206 | } 207 | } 208 | 209 | if (_proxy != NULL) { 210 | g_object_unref (_proxy); 211 | _proxy = NULL; 212 | } 213 | 214 | _initted = FALSE; 215 | } 216 | 217 | /** 218 | * notify_is_initted: 219 | * 220 | * Gets whether or not libnotify is initialized. 221 | * 222 | * Returns: %TRUE if libnotify is initialized, or %FALSE otherwise. 223 | */ 224 | gboolean 225 | notify_is_initted (void) 226 | { 227 | return _initted; 228 | } 229 | 230 | /* 231 | * _notify_get_proxy: 232 | * @error: (allow-none): a location to store a #GError, or %NULL 233 | * 234 | * Synchronously creates the #GDBusProxy for the notification service, 235 | * and caches the result. 236 | * 237 | * Returns: the #GDBusProxy for the notification service, or %NULL on error 238 | */ 239 | GDBusProxy * 240 | _notify_get_proxy (GError **error) 241 | { 242 | if (_proxy != NULL) 243 | return _proxy; 244 | 245 | _proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, 246 | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, 247 | NULL, 248 | NOTIFY_DBUS_NAME, 249 | NOTIFY_DBUS_CORE_OBJECT, 250 | NOTIFY_DBUS_CORE_INTERFACE, 251 | NULL, 252 | error); 253 | if (_proxy == NULL) { 254 | return NULL; 255 | } 256 | 257 | if (!_notify_update_spec_version (error)) { 258 | g_object_unref (_proxy); 259 | _proxy = NULL; 260 | return NULL; 261 | } 262 | 263 | g_object_add_weak_pointer (G_OBJECT (_proxy), (gpointer *) &_proxy); 264 | 265 | return _proxy; 266 | } 267 | 268 | /** 269 | * notify_get_server_caps: 270 | * 271 | * Synchronously queries the server for its capabilities and returns them in a #GList. 272 | * 273 | * Returns: (transfer full) (element-type utf8): a #GList of server capability strings. Free 274 | * the list elements with g_free() and the list itself with g_list_free(). 275 | */ 276 | GList * 277 | notify_get_server_caps (void) 278 | { 279 | GDBusProxy *proxy; 280 | GVariant *result; 281 | char **cap, **caps; 282 | GList *list = NULL; 283 | 284 | proxy = _notify_get_proxy (NULL); 285 | if (proxy == NULL) { 286 | g_warning ("Failed to connect to proxy"); 287 | return NULL; 288 | } 289 | 290 | result = g_dbus_proxy_call_sync (proxy, 291 | "GetCapabilities", 292 | g_variant_new ("()"), 293 | G_DBUS_CALL_FLAGS_NONE, 294 | -1 /* FIXME shorter timeout? */, 295 | NULL, 296 | NULL); 297 | if (result == NULL) { 298 | return NULL; 299 | } 300 | if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(as)"))) { 301 | g_variant_unref (result); 302 | return NULL; 303 | } 304 | 305 | g_variant_get (result, "(^as)", &caps); 306 | 307 | for (cap = caps; *cap != NULL; cap++) { 308 | list = g_list_prepend (list, *cap); 309 | } 310 | 311 | g_free (caps); 312 | g_variant_unref (result); 313 | 314 | return g_list_reverse (list); 315 | } 316 | 317 | /** 318 | * notify_get_server_info: 319 | * @ret_name: (out) (allow-none) (transfer full): a location to store the server name, or %NULL 320 | * @ret_vendor: (out) (allow-none) (transfer full): a location to store the server vendor, or %NULL 321 | * @ret_version: (out) (allow-none) (transfer full): a location to store the server version, or %NULL 322 | * @ret_spec_version: (out) (allow-none) (transfer full): a location to store the version the service is compliant with, or %NULL 323 | * 324 | * Synchronously queries the server for its information, specifically, the name, vendor, 325 | * server version, and the version of the notifications specification that it 326 | * is compliant with. 327 | * 328 | * Returns: %TRUE if successful, and the variables passed will be set, %FALSE 329 | * on error. The returned strings must be freed with g_free 330 | */ 331 | gboolean 332 | notify_get_server_info (char **ret_name, 333 | char **ret_vendor, 334 | char **ret_version, 335 | char **ret_spec_version) 336 | { 337 | return _notify_get_server_info (ret_name, ret_vendor, ret_version, ret_spec_version, NULL); 338 | } 339 | 340 | void 341 | _notify_cache_add_notification (NotifyNotification *n) 342 | { 343 | _active_notifications = g_list_prepend (_active_notifications, n); 344 | } 345 | 346 | void 347 | _notify_cache_remove_notification (NotifyNotification *n) 348 | { 349 | _active_notifications = g_list_remove (_active_notifications, n); 350 | } 351 | -------------------------------------------------------------------------------- /libnotify/notification.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- 2 | * 3 | * Copyright (C) 2006 Christian Hammond 4 | * Copyright (C) 2006 John Palmieri 5 | * Copyright (C) 2010 Red Hat, Inc. 6 | * Copyright © 2010 Christian Persch 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the 20 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 21 | * Boston, MA 02111-1307, USA. 22 | */ 23 | 24 | #include "config.h" 25 | 26 | #include 27 | 28 | #include "notify.h" 29 | #include "internal.h" 30 | 31 | 32 | /** 33 | * SECTION:notification 34 | * @Short_description: A passive pop-up notification. 35 | * @Title: NotifyNotification 36 | * 37 | * #NotifyNotification represents a passive pop-up notification. It can 38 | * contain summary text, body text, and an icon, as well as hints specifying 39 | * how the notification should be presented. The notification is rendered 40 | * by a notification daemon, and may present the notification in any number 41 | * of ways. As such, there is a clear separation of content and presentation, 42 | * and this API enforces that. 43 | */ 44 | 45 | 46 | #if !defined(G_PARAM_STATIC_NAME) && !defined(G_PARAM_STATIC_NICK) && \ 47 | !defined(G_PARAM_STATIC_BLURB) 48 | # define G_PARAM_STATIC_NAME 0 49 | # define G_PARAM_STATIC_NICK 0 50 | # define G_PARAM_STATIC_BLURB 0 51 | #endif 52 | 53 | static void notify_notification_class_init (NotifyNotificationClass *klass); 54 | static void notify_notification_init (NotifyNotification *sp); 55 | static void notify_notification_finalize (GObject *object); 56 | 57 | typedef struct 58 | { 59 | NotifyActionCallback cb; 60 | GFreeFunc free_func; 61 | gpointer user_data; 62 | 63 | } CallbackPair; 64 | 65 | struct _NotifyNotificationPrivate 66 | { 67 | guint32 id; 68 | char *app_name; 69 | char *summary; 70 | char *body; 71 | 72 | /* NULL to use icon data. Anything else to have server lookup icon */ 73 | char *icon_name; 74 | 75 | /* 76 | * -1 = use server default 77 | * 0 = never timeout 78 | * > 0 = Number of milliseconds before we timeout 79 | */ 80 | gint timeout; 81 | 82 | GSList *actions; 83 | GHashTable *action_map; 84 | GHashTable *hints; 85 | 86 | gboolean has_nondefault_actions; 87 | gboolean updates_pending; 88 | 89 | gulong proxy_signal_handler; 90 | 91 | gint closed_reason; 92 | }; 93 | 94 | enum 95 | { 96 | SIGNAL_CLOSED, 97 | LAST_SIGNAL 98 | }; 99 | 100 | enum 101 | { 102 | PROP_0, 103 | PROP_ID, 104 | PROP_APP_NAME, 105 | PROP_SUMMARY, 106 | PROP_BODY, 107 | PROP_ICON_NAME, 108 | PROP_CLOSED_REASON 109 | }; 110 | 111 | static void notify_notification_set_property (GObject *object, 112 | guint prop_id, 113 | const GValue *value, 114 | GParamSpec *pspec); 115 | static void notify_notification_get_property (GObject *object, 116 | guint prop_id, 117 | GValue *value, 118 | GParamSpec *pspec); 119 | static guint signals[LAST_SIGNAL] = { 0 }; 120 | 121 | static GObjectClass *parent_class = NULL; 122 | 123 | G_DEFINE_TYPE (NotifyNotification, notify_notification, G_TYPE_OBJECT) 124 | 125 | static GObject * 126 | notify_notification_constructor (GType type, 127 | guint n_construct_properties, 128 | GObjectConstructParam *construct_params) 129 | { 130 | GObject *object; 131 | 132 | object = parent_class->constructor (type, 133 | n_construct_properties, 134 | construct_params); 135 | 136 | _notify_cache_add_notification (NOTIFY_NOTIFICATION (object)); 137 | 138 | return object; 139 | } 140 | 141 | static void 142 | notify_notification_class_init (NotifyNotificationClass *klass) 143 | { 144 | GObjectClass *object_class = G_OBJECT_CLASS (klass); 145 | 146 | parent_class = g_type_class_peek_parent (klass); 147 | 148 | object_class->constructor = notify_notification_constructor; 149 | object_class->get_property = notify_notification_get_property; 150 | object_class->set_property = notify_notification_set_property; 151 | object_class->finalize = notify_notification_finalize; 152 | 153 | /** 154 | * NotifyNotification::closed: 155 | * @notification: The object which received the signal. 156 | * 157 | * Emitted when the notification is closed. 158 | */ 159 | signals[SIGNAL_CLOSED] = 160 | g_signal_new ("closed", 161 | G_TYPE_FROM_CLASS (object_class), 162 | G_SIGNAL_RUN_FIRST, 163 | G_STRUCT_OFFSET (NotifyNotificationClass, closed), 164 | NULL, 165 | NULL, 166 | g_cclosure_marshal_VOID__VOID, 167 | G_TYPE_NONE, 168 | 0); 169 | 170 | g_object_class_install_property (object_class, 171 | PROP_ID, 172 | g_param_spec_int ("id", "ID", 173 | "The notification ID", 174 | 0, 175 | G_MAXINT32, 176 | 0, 177 | G_PARAM_READWRITE 178 | | G_PARAM_CONSTRUCT 179 | | G_PARAM_STATIC_NAME 180 | | G_PARAM_STATIC_NICK 181 | | G_PARAM_STATIC_BLURB)); 182 | 183 | g_object_class_install_property (object_class, 184 | PROP_APP_NAME, 185 | g_param_spec_string ("app-name", 186 | "Application name", 187 | "The application name to use for this notification", 188 | NULL, 189 | G_PARAM_READWRITE 190 | | G_PARAM_STATIC_NAME 191 | | G_PARAM_STATIC_NICK 192 | | G_PARAM_STATIC_BLURB)); 193 | 194 | g_object_class_install_property (object_class, 195 | PROP_SUMMARY, 196 | g_param_spec_string ("summary", 197 | "Summary", 198 | "The summary text", 199 | NULL, 200 | G_PARAM_READWRITE 201 | | G_PARAM_CONSTRUCT 202 | | G_PARAM_STATIC_NAME 203 | | G_PARAM_STATIC_NICK 204 | | G_PARAM_STATIC_BLURB)); 205 | 206 | g_object_class_install_property (object_class, 207 | PROP_BODY, 208 | g_param_spec_string ("body", 209 | "Message Body", 210 | "The message body text", 211 | NULL, 212 | G_PARAM_READWRITE 213 | | G_PARAM_CONSTRUCT 214 | | G_PARAM_STATIC_NAME 215 | | G_PARAM_STATIC_NICK 216 | | G_PARAM_STATIC_BLURB)); 217 | 218 | g_object_class_install_property (object_class, 219 | PROP_ICON_NAME, 220 | g_param_spec_string ("icon-name", 221 | "Icon Name", 222 | "The icon filename or icon theme-compliant name", 223 | NULL, 224 | G_PARAM_READWRITE 225 | | G_PARAM_CONSTRUCT 226 | | G_PARAM_STATIC_NAME 227 | | G_PARAM_STATIC_NICK 228 | | G_PARAM_STATIC_BLURB)); 229 | 230 | g_object_class_install_property (object_class, 231 | PROP_CLOSED_REASON, 232 | g_param_spec_int ("closed-reason", 233 | "Closed Reason", 234 | "The reason code for why the notification was closed", 235 | -1, 236 | G_MAXINT32, 237 | -1, 238 | G_PARAM_READABLE 239 | | G_PARAM_STATIC_NAME 240 | | G_PARAM_STATIC_NICK 241 | | G_PARAM_STATIC_BLURB)); 242 | } 243 | 244 | static void 245 | notify_notification_update_internal (NotifyNotification *notification, 246 | const char *app_name, 247 | const char *summary, 248 | const char *body, 249 | const char *icon); 250 | 251 | static void 252 | notify_notification_set_property (GObject *object, 253 | guint prop_id, 254 | const GValue *value, 255 | GParamSpec *pspec) 256 | { 257 | NotifyNotification *notification = NOTIFY_NOTIFICATION (object); 258 | NotifyNotificationPrivate *priv = notification->priv; 259 | 260 | switch (prop_id) { 261 | case PROP_ID: 262 | priv->id = g_value_get_int (value); 263 | break; 264 | 265 | case PROP_APP_NAME: 266 | notify_notification_update_internal (notification, 267 | g_value_get_string (value), 268 | priv->summary, 269 | priv->body, 270 | priv->icon_name); 271 | break; 272 | 273 | case PROP_SUMMARY: 274 | notify_notification_update_internal (notification, 275 | priv->app_name, 276 | g_value_get_string (value), 277 | priv->body, 278 | priv->icon_name); 279 | break; 280 | 281 | case PROP_BODY: 282 | notify_notification_update_internal (notification, 283 | priv->app_name, 284 | priv->summary, 285 | g_value_get_string (value), 286 | priv->icon_name); 287 | break; 288 | 289 | case PROP_ICON_NAME: 290 | notify_notification_update_internal (notification, 291 | priv->app_name, 292 | priv->summary, 293 | priv->body, 294 | g_value_get_string (value)); 295 | break; 296 | 297 | default: 298 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 299 | break; 300 | } 301 | } 302 | 303 | static void 304 | notify_notification_get_property (GObject *object, 305 | guint prop_id, 306 | GValue *value, 307 | GParamSpec *pspec) 308 | { 309 | NotifyNotification *notification = NOTIFY_NOTIFICATION (object); 310 | NotifyNotificationPrivate *priv = notification->priv; 311 | 312 | switch (prop_id) { 313 | case PROP_ID: 314 | g_value_set_int (value, priv->id); 315 | break; 316 | 317 | case PROP_SUMMARY: 318 | g_value_set_string (value, priv->summary); 319 | break; 320 | 321 | case PROP_APP_NAME: 322 | g_value_set_string (value, priv->app_name); 323 | break; 324 | 325 | case PROP_BODY: 326 | g_value_set_string (value, priv->body); 327 | break; 328 | 329 | case PROP_ICON_NAME: 330 | g_value_set_string (value, priv->icon_name); 331 | break; 332 | 333 | case PROP_CLOSED_REASON: 334 | g_value_set_int (value, priv->closed_reason); 335 | break; 336 | 337 | default: 338 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 339 | break; 340 | } 341 | } 342 | 343 | static void 344 | destroy_pair (CallbackPair *pair) 345 | { 346 | if (pair->user_data != NULL && pair->free_func != NULL) { 347 | pair->free_func (pair->user_data); 348 | } 349 | 350 | g_free (pair); 351 | } 352 | 353 | static void 354 | notify_notification_init (NotifyNotification *obj) 355 | { 356 | obj->priv = g_new0 (NotifyNotificationPrivate, 1); 357 | obj->priv->timeout = NOTIFY_EXPIRES_DEFAULT; 358 | obj->priv->closed_reason = -1; 359 | obj->priv->hints = g_hash_table_new_full (g_str_hash, 360 | g_str_equal, 361 | g_free, 362 | (GDestroyNotify) g_variant_unref); 363 | 364 | obj->priv->action_map = g_hash_table_new_full (g_str_hash, 365 | g_str_equal, 366 | g_free, 367 | (GDestroyNotify) destroy_pair); 368 | } 369 | 370 | static void 371 | notify_notification_finalize (GObject *object) 372 | { 373 | NotifyNotification *obj = NOTIFY_NOTIFICATION (object); 374 | NotifyNotificationPrivate *priv = obj->priv; 375 | GDBusProxy *proxy; 376 | 377 | _notify_cache_remove_notification (obj); 378 | 379 | g_free (priv->app_name); 380 | g_free (priv->summary); 381 | g_free (priv->body); 382 | g_free (priv->icon_name); 383 | 384 | if (priv->actions != NULL) { 385 | g_slist_foreach (priv->actions, (GFunc) g_free, NULL); 386 | g_slist_free (priv->actions); 387 | } 388 | 389 | if (priv->action_map != NULL) 390 | g_hash_table_destroy (priv->action_map); 391 | 392 | if (priv->hints != NULL) 393 | g_hash_table_destroy (priv->hints); 394 | 395 | proxy = _notify_get_proxy (NULL); 396 | if (proxy != NULL && priv->proxy_signal_handler != 0) { 397 | g_signal_handler_disconnect (proxy, priv->proxy_signal_handler); 398 | } 399 | 400 | g_free (obj->priv); 401 | 402 | G_OBJECT_CLASS (parent_class)->finalize (object); 403 | } 404 | 405 | /** 406 | * notify_notification_new: 407 | * @summary: The required summary text. 408 | * @body: (allow-none): The optional body text. 409 | * @icon: (allow-none): The optional icon theme icon name or filename. 410 | * 411 | * Creates a new #NotifyNotification. The summary text is required, but 412 | * all other parameters are optional. 413 | * 414 | * Returns: The new #NotifyNotification. 415 | */ 416 | NotifyNotification * 417 | notify_notification_new (const char *summary, 418 | const char *body, 419 | const char *icon) 420 | { 421 | return g_object_new (NOTIFY_TYPE_NOTIFICATION, 422 | "summary", summary, 423 | "body", body, 424 | "icon-name", icon, 425 | NULL); 426 | } 427 | 428 | static gchar * 429 | try_prepend_path (const char *base_path, 430 | const char *path) 431 | { 432 | gchar *path_filename; 433 | gchar *path_ret; 434 | 435 | if (!path || *path == '\0') 436 | return NULL; 437 | 438 | path_ret = NULL; 439 | path_filename = g_filename_from_uri (base_path, NULL, NULL); 440 | 441 | if (path_filename == NULL) { 442 | if (base_path && base_path[0] == G_DIR_SEPARATOR) { 443 | path_filename = g_strdup (base_path); 444 | } else { 445 | path_filename = realpath (base_path, NULL); 446 | } 447 | } 448 | 449 | g_debug ("Trying to look at file '%s' in the '%s' prefix.", 450 | base_path, 451 | path); 452 | 453 | path_ret = g_build_filename (path, path_filename, NULL); 454 | 455 | if (!g_file_test (path_ret, G_FILE_TEST_EXISTS)) { 456 | g_free (path_ret); 457 | path_ret = NULL; 458 | } 459 | 460 | g_free (path_filename); 461 | 462 | return path_ret; 463 | } 464 | 465 | static gchar * 466 | try_prepend_desktop (const gchar *desktop) 467 | { 468 | gchar *ret; 469 | 470 | /* 471 | * if it's an absolute path, try prepending $SNAP, otherwise try 472 | * $SNAP_NAME_; snap .desktop files are in the format 473 | * ${SNAP_NAME}_desktop_file_name 474 | */ 475 | ret = try_prepend_path (desktop, g_getenv ("SNAP")); 476 | 477 | if (ret == NULL) { 478 | const gchar *snap_name = g_getenv ("SNAP_NAME"); 479 | 480 | if (snap_name != NULL && snap_name[0] != '\0') { 481 | ret = g_strdup_printf ("%s_%s", snap_name, desktop); 482 | } 483 | } 484 | 485 | return ret; 486 | } 487 | 488 | static gchar * 489 | try_prepend_snap (const gchar *value) 490 | { 491 | /* hardcoded paths to icons might be relocated under $SNAP */ 492 | return try_prepend_path (value, g_getenv ("SNAP")); 493 | } 494 | 495 | 496 | static void 497 | notify_notification_update_internal (NotifyNotification *notification, 498 | const char *app_name, 499 | const char *summary, 500 | const char *body, 501 | const char *icon) 502 | { 503 | if (notification->priv->app_name != app_name) { 504 | g_free (notification->priv->app_name); 505 | notification->priv->app_name = g_strdup (app_name); 506 | g_object_notify (G_OBJECT (notification), "app-name"); 507 | } 508 | 509 | if (notification->priv->summary != summary) { 510 | g_free (notification->priv->summary); 511 | notification->priv->summary = g_strdup (summary); 512 | g_object_notify (G_OBJECT (notification), "summary"); 513 | } 514 | 515 | if (notification->priv->body != body) { 516 | g_free (notification->priv->body); 517 | notification->priv->body = (body != NULL 518 | && *body != '\0' ? g_strdup (body) : NULL); 519 | g_object_notify (G_OBJECT (notification), "body"); 520 | } 521 | 522 | if (notification->priv->icon_name != icon) { 523 | gchar *snapped_icon; 524 | g_free (notification->priv->icon_name); 525 | notification->priv->icon_name = (icon != NULL 526 | && *icon != '\0' ? g_strdup (icon) : NULL); 527 | snapped_icon = try_prepend_desktop (notification->priv->icon_name); 528 | if (snapped_icon != NULL) { 529 | g_debug ("Icon updated in snap environment: '%s' -> '%s'\n", 530 | notification->priv->icon_name, snapped_icon); 531 | g_free (notification->priv->icon_name); 532 | notification->priv->icon_name = snapped_icon; 533 | } 534 | g_object_notify (G_OBJECT (notification), "icon-name"); 535 | } 536 | 537 | notification->priv->updates_pending = TRUE; 538 | } 539 | 540 | /** 541 | * notify_notification_update: 542 | * @notification: The notification to update. 543 | * @summary: The new required summary text. 544 | * @body: (allow-none): The optional body text. 545 | * @icon: (allow-none): The optional icon theme icon name or filename. 546 | * 547 | * Updates the notification text and icon. This won't send the update out 548 | * and display it on the screen. For that, you will need to call 549 | * notify_notification_show(). 550 | * 551 | * Returns: %TRUE, unless an invalid parameter was passed. 552 | */ 553 | gboolean 554 | notify_notification_update (NotifyNotification *notification, 555 | const char *summary, 556 | const char *body, 557 | const char *icon) 558 | { 559 | g_return_val_if_fail (notification != NULL, FALSE); 560 | g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), FALSE); 561 | g_return_val_if_fail (summary != NULL && *summary != '\0', FALSE); 562 | 563 | notify_notification_update_internal (notification, 564 | notification->priv->app_name, 565 | summary, body, icon); 566 | 567 | return TRUE; 568 | } 569 | 570 | static void 571 | proxy_g_signal_cb (GDBusProxy *proxy, 572 | const char *sender_name, 573 | const char *signal_name, 574 | GVariant *parameters, 575 | NotifyNotification *notification) 576 | { 577 | g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); 578 | 579 | if (g_strcmp0 (signal_name, "NotificationClosed") == 0 && 580 | g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)"))) { 581 | guint32 id, reason; 582 | 583 | g_variant_get (parameters, "(uu)", &id, &reason); 584 | if (id != notification->priv->id) 585 | return; 586 | 587 | g_object_ref (G_OBJECT (notification)); 588 | notification->priv->closed_reason = reason; 589 | g_signal_emit (notification, signals[SIGNAL_CLOSED], 0); 590 | notification->priv->id = 0; 591 | g_object_unref (G_OBJECT (notification)); 592 | } else if (g_strcmp0 (signal_name, "ActionInvoked") == 0 && 593 | g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(us)"))) { 594 | guint32 id; 595 | const char *action; 596 | CallbackPair *pair; 597 | 598 | g_variant_get (parameters, "(u&s)", &id, &action); 599 | 600 | if (id != notification->priv->id) 601 | return; 602 | 603 | pair = (CallbackPair *) g_hash_table_lookup (notification->priv->action_map, 604 | action); 605 | 606 | if (pair == NULL) { 607 | if (g_ascii_strcasecmp (action, "default")) { 608 | g_warning ("Received unknown action %s", action); 609 | } 610 | } else { 611 | pair->cb (notification, (char *) action, pair->user_data); 612 | } 613 | } 614 | } 615 | 616 | /** 617 | * notify_notification_show: 618 | * @notification: The notification. 619 | * @error: The returned error information. 620 | * 621 | * Tells the notification server to display the notification on the screen. 622 | * 623 | * Returns: %TRUE if successful. On error, this will return %FALSE and set 624 | * @error. 625 | */ 626 | gboolean 627 | notify_notification_show (NotifyNotification *notification, 628 | GError **error) 629 | { 630 | NotifyNotificationPrivate *priv; 631 | GDBusProxy *proxy; 632 | GVariantBuilder actions_builder, hints_builder; 633 | GSList *l; 634 | GHashTableIter iter; 635 | gpointer key, data; 636 | GVariant *result; 637 | 638 | g_return_val_if_fail (notification != NULL, FALSE); 639 | g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), FALSE); 640 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); 641 | 642 | if (!notify_is_initted ()) { 643 | g_warning ("you must call notify_init() before showing"); 644 | g_assert_not_reached (); 645 | } 646 | 647 | priv = notification->priv; 648 | proxy = _notify_get_proxy (error); 649 | if (proxy == NULL) { 650 | return FALSE; 651 | } 652 | 653 | if (priv->proxy_signal_handler == 0) { 654 | priv->proxy_signal_handler = g_signal_connect (proxy, 655 | "g-signal", 656 | G_CALLBACK (proxy_g_signal_cb), 657 | notification); 658 | } 659 | 660 | g_variant_builder_init (&actions_builder, G_VARIANT_TYPE ("as")); 661 | for (l = priv->actions; l != NULL; l = l->next) { 662 | g_variant_builder_add (&actions_builder, "s", l->data); 663 | } 664 | 665 | g_variant_builder_init (&hints_builder, G_VARIANT_TYPE ("a{sv}")); 666 | g_hash_table_iter_init (&iter, priv->hints); 667 | while (g_hash_table_iter_next (&iter, &key, &data)) { 668 | g_variant_builder_add (&hints_builder, "{sv}", key, data); 669 | } 670 | 671 | /* TODO: make this nonblocking */ 672 | result = g_dbus_proxy_call_sync (proxy, 673 | "Notify", 674 | g_variant_new ("(susssasa{sv}i)", 675 | priv->app_name ? priv->app_name : notify_get_app_name (), 676 | priv->id, 677 | priv->icon_name ? priv->icon_name : "", 678 | priv->summary ? priv->summary : "", 679 | priv->body ? priv->body : "", 680 | &actions_builder, 681 | &hints_builder, 682 | priv->timeout), 683 | G_DBUS_CALL_FLAGS_NONE, 684 | -1 /* FIXME ? */, 685 | NULL, 686 | error); 687 | if (result == NULL) { 688 | return FALSE; 689 | } 690 | if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(u)"))) { 691 | g_variant_unref (result); 692 | g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, 693 | "Unexpected reply type"); 694 | return FALSE; 695 | } 696 | 697 | g_variant_get (result, "(u)", &priv->id); 698 | g_variant_unref (result); 699 | 700 | return TRUE; 701 | } 702 | 703 | /** 704 | * notify_notification_set_timeout: 705 | * @notification: The notification. 706 | * @timeout: The timeout in milliseconds. 707 | * 708 | * Sets the timeout of the notification. To set the default time, pass 709 | * %NOTIFY_EXPIRES_DEFAULT as @timeout. To set the notification to never 710 | * expire, pass %NOTIFY_EXPIRES_NEVER. 711 | * 712 | * Note that the timeout may be ignored by the server. 713 | */ 714 | void 715 | notify_notification_set_timeout (NotifyNotification *notification, 716 | gint timeout) 717 | { 718 | g_return_if_fail (notification != NULL); 719 | g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); 720 | 721 | notification->priv->timeout = timeout; 722 | } 723 | 724 | gint 725 | _notify_notification_get_timeout (const NotifyNotification *notification) 726 | { 727 | g_return_val_if_fail (notification != NULL, -1); 728 | g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), -1); 729 | 730 | return notification->priv->timeout; 731 | } 732 | 733 | /** 734 | * notify_notification_set_category: 735 | * @notification: The notification. 736 | * @category: The category. 737 | * 738 | * Sets the category of this notification. This can be used by the 739 | * notification server to filter or display the data in a certain way. 740 | */ 741 | void 742 | notify_notification_set_category (NotifyNotification *notification, 743 | const char *category) 744 | { 745 | g_return_if_fail (notification != NULL); 746 | g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); 747 | 748 | if (category != NULL && category[0] != '\0') { 749 | notify_notification_set_hint_string (notification, 750 | "category", 751 | category); 752 | } 753 | } 754 | 755 | /** 756 | * notify_notification_set_urgency: 757 | * @notification: The notification. 758 | * @urgency: The urgency level. 759 | * 760 | * Sets the urgency level of this notification. 761 | * 762 | * See: #NotifyUrgency 763 | */ 764 | void 765 | notify_notification_set_urgency (NotifyNotification *notification, 766 | NotifyUrgency urgency) 767 | { 768 | g_return_if_fail (notification != NULL); 769 | g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); 770 | 771 | notify_notification_set_hint_byte (notification, 772 | "urgency", 773 | (guchar) urgency); 774 | } 775 | 776 | /** 777 | * notify_notification_set_icon_from_pixbuf: 778 | * @notification: The notification. 779 | * @icon: The icon. 780 | * 781 | * Sets the icon in the notification from a #GdkPixbuf. 782 | * Deprecated: use notify_notification_set_image_from_pixbuf() instead. 783 | * 784 | */ 785 | void 786 | notify_notification_set_icon_from_pixbuf (NotifyNotification *notification, 787 | GdkPixbuf *icon) 788 | { 789 | notify_notification_set_image_from_pixbuf (notification, icon); 790 | } 791 | 792 | /** 793 | * notify_notification_set_image_from_pixbuf: 794 | * @notification: The notification. 795 | * @pixbuf: The image. 796 | * 797 | * Sets the image in the notification from a #GdkPixbuf. 798 | * 799 | */ 800 | void 801 | notify_notification_set_image_from_pixbuf (NotifyNotification *notification, 802 | GdkPixbuf *pixbuf) 803 | { 804 | gint width; 805 | gint height; 806 | gint rowstride; 807 | gint bits_per_sample; 808 | gint n_channels; 809 | guchar *image; 810 | gboolean has_alpha; 811 | gsize image_len; 812 | GVariant *value; 813 | const char *hint_name; 814 | 815 | g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf)); 816 | 817 | if (_notify_check_spec_version(1, 2)) { 818 | hint_name = "image-data"; 819 | } else if (_notify_check_spec_version(1, 1)) { 820 | hint_name = "image_data"; 821 | } else { 822 | hint_name = "icon_data"; 823 | } 824 | 825 | if (pixbuf == NULL) { 826 | notify_notification_set_hint (notification, hint_name, NULL); 827 | return; 828 | } 829 | 830 | g_object_get (pixbuf, 831 | "width", &width, 832 | "height", &height, 833 | "rowstride", &rowstride, 834 | "n-channels", &n_channels, 835 | "bits-per-sample", &bits_per_sample, 836 | "pixels", &image, 837 | "has-alpha", &has_alpha, 838 | NULL); 839 | image_len = (height - 1) * rowstride + width * 840 | ((n_channels * bits_per_sample + 7) / 8); 841 | 842 | value = g_variant_new ("(iiibii@ay)", 843 | width, 844 | height, 845 | rowstride, 846 | has_alpha, 847 | bits_per_sample, 848 | n_channels, 849 | g_variant_new_from_data (G_VARIANT_TYPE ("ay"), 850 | image, 851 | image_len, 852 | TRUE, 853 | (GDestroyNotify) g_object_unref, 854 | g_object_ref (pixbuf))); 855 | notify_notification_set_hint (notification, hint_name, value); 856 | } 857 | 858 | static GVariant * 859 | get_parsed_variant (GVariant *variant, 860 | gchar *(*str_parser)(const gchar *)) 861 | { 862 | gchar *parsed = str_parser (g_variant_get_string (variant, NULL)); 863 | 864 | if (parsed != NULL) { 865 | g_variant_unref (variant); 866 | variant = g_variant_new_take_string (parsed); 867 | } 868 | 869 | return variant; 870 | } 871 | 872 | static GVariant * 873 | maybe_parse_snap_hint_value (const gchar *key, 874 | GVariant *value) 875 | { 876 | if (g_strcmp0 (key, "desktop-entry") == 0) { 877 | value = get_parsed_variant (value, try_prepend_desktop); 878 | } else if (g_strcmp0 (key, "image-path") == 0 || 879 | g_strcmp0 (key, "image_path") == 0 || 880 | g_strcmp0 (key, "sound-file") == 0) { 881 | value = get_parsed_variant (value, try_prepend_snap); 882 | } 883 | 884 | return value; 885 | } 886 | 887 | /** 888 | * notify_notification_set_hint: 889 | * @notification: a #NotifyNotification 890 | * @key: the hint key 891 | * @value: (allow-none): the hint value, or %NULL to unset the hint 892 | * 893 | * Sets a hint for @key with value @value. If @value is %NULL, 894 | * a previously set hint for @key is unset. 895 | * 896 | * If @value is floating, it is consumed. 897 | * 898 | * Since: 0.6 899 | */ 900 | void 901 | notify_notification_set_hint (NotifyNotification *notification, 902 | const char *key, 903 | GVariant *value) 904 | { 905 | g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); 906 | g_return_if_fail (key != NULL && *key != '\0'); 907 | 908 | if (value != NULL) { 909 | value = maybe_parse_snap_hint_value (key, value); 910 | g_hash_table_insert (notification->priv->hints, 911 | g_strdup (key), 912 | g_variant_ref_sink (value)); 913 | } else { 914 | g_hash_table_remove (notification->priv->hints, key); 915 | } 916 | } 917 | 918 | /** 919 | * notify_notification_set_app_name: 920 | * @notification: a #NotifyNotification 921 | * @app_name: the localised application name 922 | * 923 | * Sets the application name for the notification. If this function is 924 | * not called or if @app_name is %NULL, the application name will be 925 | * set from the value used in notify_init() or overridden with 926 | * notify_set_app_name(). 927 | * 928 | * Since: 0.7.3 929 | */ 930 | void 931 | notify_notification_set_app_name (NotifyNotification *notification, 932 | const char *app_name) 933 | { 934 | g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); 935 | 936 | g_free (notification->priv->app_name); 937 | notification->priv->app_name = g_strdup (app_name); 938 | 939 | g_object_notify (G_OBJECT (notification), "app-name"); 940 | } 941 | 942 | /** 943 | * notify_notification_set_hint_int32: 944 | * @notification: The notification. 945 | * @key: The hint. 946 | * @value: The hint's value. 947 | * 948 | * Sets a hint with a 32-bit integer value. 949 | * 950 | * Deprecated: 0.6. Use notify_notification_set_hint() instead 951 | */ 952 | void 953 | notify_notification_set_hint_int32 (NotifyNotification *notification, 954 | const char *key, 955 | gint value) 956 | { 957 | notify_notification_set_hint (notification, key, 958 | g_variant_new_int32 (value)); 959 | } 960 | 961 | 962 | /** 963 | * notify_notification_set_hint_uint32: 964 | * @notification: The notification. 965 | * @key: The hint. 966 | * @value: The hint's value. 967 | * 968 | * Sets a hint with an unsigned 32-bit integer value. 969 | * 970 | * Deprecated: 0.6. Use notify_notification_set_hint() instead 971 | */ 972 | void 973 | notify_notification_set_hint_uint32 (NotifyNotification *notification, 974 | const char *key, 975 | guint value) 976 | { 977 | notify_notification_set_hint (notification, key, 978 | g_variant_new_uint32 (value)); 979 | } 980 | 981 | /** 982 | * notify_notification_set_hint_double: 983 | * @notification: The notification. 984 | * @key: The hint. 985 | * @value: The hint's value. 986 | * 987 | * Sets a hint with a double value. 988 | * 989 | * Deprecated: 0.6. Use notify_notification_set_hint() instead 990 | */ 991 | void 992 | notify_notification_set_hint_double (NotifyNotification *notification, 993 | const char *key, 994 | gdouble value) 995 | { 996 | notify_notification_set_hint (notification, key, 997 | g_variant_new_double (value)); 998 | } 999 | 1000 | /** 1001 | * notify_notification_set_hint_byte: 1002 | * @notification: The notification. 1003 | * @key: The hint. 1004 | * @value: The hint's value. 1005 | * 1006 | * Sets a hint with a byte value. 1007 | * 1008 | * Deprecated: 0.6. Use notify_notification_set_hint() instead 1009 | */ 1010 | void 1011 | notify_notification_set_hint_byte (NotifyNotification *notification, 1012 | const char *key, 1013 | guchar value) 1014 | { 1015 | notify_notification_set_hint (notification, key, 1016 | g_variant_new_byte (value)); 1017 | } 1018 | 1019 | /** 1020 | * notify_notification_set_hint_byte_array: 1021 | * @notification: The notification. 1022 | * @key: The hint. 1023 | * @value: (array length=len): The hint's value. 1024 | * @len: The length of the byte array. 1025 | * 1026 | * Sets a hint with a byte array value. The length of @value must be passed 1027 | * as @len. 1028 | * 1029 | * Deprecated: 0.6. Use notify_notification_set_hint() instead 1030 | */ 1031 | void 1032 | notify_notification_set_hint_byte_array (NotifyNotification *notification, 1033 | const char *key, 1034 | const guchar *value, 1035 | gsize len) 1036 | { 1037 | gpointer value_dup; 1038 | 1039 | g_return_if_fail (value != NULL || len == 0); 1040 | 1041 | value_dup = g_memdup (value, len); 1042 | notify_notification_set_hint (notification, key, 1043 | g_variant_new_from_data (G_VARIANT_TYPE ("ay"), 1044 | value_dup, 1045 | len, 1046 | TRUE, 1047 | g_free, 1048 | value_dup)); 1049 | } 1050 | 1051 | /** 1052 | * notify_notification_set_hint_string: 1053 | * @notification: The notification. 1054 | * @key: The hint. 1055 | * @value: The hint's value. 1056 | * 1057 | * Sets a hint with a string value. 1058 | * 1059 | * Deprecated: 0.6. Use notify_notification_set_hint() instead 1060 | */ 1061 | void 1062 | notify_notification_set_hint_string (NotifyNotification *notification, 1063 | const char *key, 1064 | const char *value) 1065 | { 1066 | if (value != NULL && value[0] != '\0') { 1067 | notify_notification_set_hint (notification, 1068 | key, 1069 | g_variant_new_string (value)); 1070 | } 1071 | } 1072 | 1073 | static gboolean 1074 | _remove_all (void) 1075 | { 1076 | return TRUE; 1077 | } 1078 | 1079 | /** 1080 | * notify_notification_clear_hints: 1081 | * @notification: The notification. 1082 | * 1083 | * Clears all hints from the notification. 1084 | */ 1085 | void 1086 | notify_notification_clear_hints (NotifyNotification *notification) 1087 | { 1088 | g_return_if_fail (notification != NULL); 1089 | g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); 1090 | 1091 | g_hash_table_foreach_remove (notification->priv->hints, 1092 | (GHRFunc) _remove_all, 1093 | NULL); 1094 | } 1095 | 1096 | /** 1097 | * notify_notification_clear_actions: 1098 | * @notification: The notification. 1099 | * 1100 | * Clears all actions from the notification. 1101 | */ 1102 | void 1103 | notify_notification_clear_actions (NotifyNotification *notification) 1104 | { 1105 | g_return_if_fail (notification != NULL); 1106 | g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); 1107 | 1108 | g_hash_table_foreach_remove (notification->priv->action_map, 1109 | (GHRFunc) _remove_all, 1110 | NULL); 1111 | 1112 | if (notification->priv->actions != NULL) { 1113 | g_slist_foreach (notification->priv->actions, 1114 | (GFunc) g_free, 1115 | NULL); 1116 | g_slist_free (notification->priv->actions); 1117 | } 1118 | 1119 | notification->priv->actions = NULL; 1120 | notification->priv->has_nondefault_actions = FALSE; 1121 | } 1122 | 1123 | /** 1124 | * notify_notification_add_action: 1125 | * @notification: The notification. 1126 | * @action: The action ID. 1127 | * @label: The human-readable action label. 1128 | * @callback: The action's callback function. 1129 | * @user_data: Optional custom data to pass to @callback. 1130 | * @free_func: (type GLib.DestroyNotify): An optional function to free @user_data when the notification 1131 | * is destroyed. 1132 | * 1133 | * Adds an action to a notification. When the action is invoked, the 1134 | * specified callback function will be called, along with the value passed 1135 | * to @user_data. 1136 | */ 1137 | void 1138 | notify_notification_add_action (NotifyNotification *notification, 1139 | const char *action, 1140 | const char *label, 1141 | NotifyActionCallback callback, 1142 | gpointer user_data, 1143 | GFreeFunc free_func) 1144 | { 1145 | NotifyNotificationPrivate *priv; 1146 | CallbackPair *pair; 1147 | 1148 | g_return_if_fail (NOTIFY_IS_NOTIFICATION (notification)); 1149 | g_return_if_fail (action != NULL && *action != '\0'); 1150 | g_return_if_fail (label != NULL && *label != '\0'); 1151 | g_return_if_fail (callback != NULL); 1152 | 1153 | priv = notification->priv; 1154 | 1155 | priv->actions = g_slist_append (priv->actions, g_strdup (action)); 1156 | priv->actions = g_slist_append (priv->actions, g_strdup (label)); 1157 | 1158 | pair = g_new0 (CallbackPair, 1); 1159 | pair->cb = callback; 1160 | pair->user_data = user_data; 1161 | pair->free_func = free_func; 1162 | g_hash_table_insert (priv->action_map, g_strdup (action), pair); 1163 | 1164 | if (!notification->priv->has_nondefault_actions && 1165 | g_ascii_strcasecmp (action, "default") != 0) { 1166 | notification->priv->has_nondefault_actions = TRUE; 1167 | } 1168 | } 1169 | 1170 | gboolean 1171 | _notify_notification_has_nondefault_actions (const NotifyNotification *n) 1172 | { 1173 | g_return_val_if_fail (n != NULL, FALSE); 1174 | g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (n), FALSE); 1175 | 1176 | return n->priv->has_nondefault_actions; 1177 | } 1178 | 1179 | /** 1180 | * notify_notification_close: 1181 | * @notification: The notification. 1182 | * @error: The returned error information. 1183 | * 1184 | * Synchronously tells the notification server to hide the notification on the screen. 1185 | * 1186 | * Returns: %TRUE on success, or %FALSE on error with @error filled in 1187 | */ 1188 | gboolean 1189 | notify_notification_close (NotifyNotification *notification, 1190 | GError **error) 1191 | { 1192 | NotifyNotificationPrivate *priv; 1193 | GDBusProxy *proxy; 1194 | GVariant *result; 1195 | 1196 | g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), FALSE); 1197 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); 1198 | 1199 | priv = notification->priv; 1200 | 1201 | proxy = _notify_get_proxy (error); 1202 | if (proxy == NULL) { 1203 | return FALSE; 1204 | } 1205 | 1206 | /* FIXME: make this nonblocking! */ 1207 | result = g_dbus_proxy_call_sync (proxy, 1208 | "CloseNotification", 1209 | g_variant_new ("(u)", priv->id), 1210 | G_DBUS_CALL_FLAGS_NONE, 1211 | -1 /* FIXME! */, 1212 | NULL, 1213 | error); 1214 | if (result == NULL) { 1215 | return FALSE; 1216 | } 1217 | 1218 | g_variant_unref (result); 1219 | 1220 | return TRUE; 1221 | } 1222 | 1223 | /** 1224 | * notify_notification_get_closed_reason: 1225 | * @notification: The notification. 1226 | * 1227 | * Returns the closed reason code for the notification. This is valid only 1228 | * after the "closed" signal is emitted. 1229 | * 1230 | * Returns: The closed reason code. 1231 | */ 1232 | gint 1233 | notify_notification_get_closed_reason (const NotifyNotification *notification) 1234 | { 1235 | g_return_val_if_fail (notification != NULL, -1); 1236 | g_return_val_if_fail (NOTIFY_IS_NOTIFICATION (notification), -1); 1237 | 1238 | return notification->priv->closed_reason; 1239 | } 1240 | --------------------------------------------------------------------------------