Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-notifications"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <gio/gdesktopappinfo.h>
10 : : #include <libpeas.h>
11 : : #include <libvalent-core.h>
12 : :
13 : : #include "valent-notification.h"
14 : : #include "valent-notifications.h"
15 : : #include "valent-notifications-adapter.h"
16 : :
17 : :
18 : : /**
19 : : * ValentNotifications:
20 : : *
21 : : * A class for sending and receiving notifications.
22 : : *
23 : : * `ValentNotifications` is an aggregator of notifications, intended for use by
24 : : * [class@Valent.DevicePlugin] implementations.
25 : : *
26 : : * Plugins can implement [class@Valent.NotificationsAdapter] to provide an
27 : : * interface to monitor, send and withdraw notifications.
28 : : *
29 : : * Since: 1.0
30 : : */
31 : : struct _ValentNotifications
32 : : {
33 : : ValentComponent parent_instance;
34 : :
35 : : GVariant *applications;
36 : : };
37 : :
38 [ + + + - ]: 163 : G_DEFINE_FINAL_TYPE (ValentNotifications, valent_notifications, VALENT_TYPE_COMPONENT)
39 : :
40 : : static GVariant *
41 : 0 : app_info_serialize (GAppInfo *info)
42 : : {
43 : 0 : GVariantDict dict;
44 : 0 : const char *name;
45 : 0 : GIcon *icon;
46 : :
47 [ # # # # : 0 : g_assert (G_IS_APP_INFO (info));
# # # # ]
48 : :
49 : 0 : g_variant_dict_init (&dict, NULL);
50 : :
51 [ # # ]: 0 : if ((name = g_app_info_get_display_name (info)) != NULL)
52 : 0 : g_variant_dict_insert (&dict, "name", "s", name);
53 : :
54 [ # # ]: 0 : if ((icon = g_app_info_get_icon (info)) != NULL)
55 : : {
56 : 0 : g_autoptr (GVariant) iconv = NULL;
57 : :
58 : 0 : iconv = g_icon_serialize (icon);
59 [ # # ]: 0 : g_variant_dict_insert_value (&dict, "icon", iconv);
60 : : }
61 : :
62 : 0 : return g_variant_dict_end (&dict);
63 : : }
64 : :
65 : : static GVariant *
66 : 5 : notification_serialize (ValentNotification *notification)
67 : : {
68 : 5 : GVariantDict dict;
69 : 5 : const char *app_name;
70 : 5 : GIcon *icon;
71 : :
72 : 5 : g_variant_dict_init (&dict, NULL);
73 : :
74 [ + + ]: 5 : if ((app_name = valent_notification_get_application (notification)) != NULL)
75 : 4 : g_variant_dict_insert (&dict, "name", "s", app_name);
76 : :
77 [ + + ]: 5 : if ((icon = valent_notification_get_icon (notification)) != NULL)
78 : : {
79 : 8 : g_autoptr (GVariant) iconv = NULL;
80 : :
81 : 3 : iconv = g_icon_serialize (icon);
82 [ + - ]: 3 : g_variant_dict_insert_value (&dict, "icon", iconv);
83 : : }
84 : :
85 : 5 : return g_variant_dict_end (&dict);
86 : : }
87 : :
88 : : static void
89 : 4 : query_applications (ValentNotifications *self)
90 : : {
91 : 4 : GVariantDict dict;
92 : 8 : g_autolist (GAppInfo) infos = NULL;
93 : :
94 [ - + ]: 4 : g_assert (VALENT_IS_NOTIFICATIONS (self));
95 : :
96 : 4 : g_variant_dict_init (&dict, NULL);
97 : 4 : infos = g_app_info_get_all ();
98 : :
99 [ - + ]: 4 : for (const GList *iter = infos; iter; iter = iter->next)
100 : : {
101 : 0 : const char *desktop_id;
102 : 0 : const char *name;
103 : :
104 : 0 : desktop_id = g_app_info_get_id (iter->data);
105 : :
106 [ # # # # : 0 : if G_UNLIKELY (g_str_has_prefix (desktop_id, APPLICATION_ID))
# # ]
107 : 0 : continue;
108 : :
109 [ # # ]: 0 : if (!g_desktop_app_info_get_boolean (iter->data, "X-GNOME-UsesNotifications"))
110 : 0 : continue;
111 : :
112 : 0 : name = g_app_info_get_display_name (iter->data);
113 : 0 : g_variant_dict_insert_value (&dict, name, app_info_serialize (iter->data));
114 : : }
115 : :
116 : 4 : self->applications = g_variant_ref_sink (g_variant_dict_end (&dict));
117 : 4 : }
118 : :
119 : : static void
120 : 5 : add_application (ValentNotifications *self,
121 : : GVariant *application)
122 : : {
123 : 5 : GVariantDict dict;
124 : 5 : const char *name;
125 : :
126 [ - + ]: 5 : if (self->applications == NULL)
127 : 0 : query_applications (self);
128 : :
129 : 5 : g_variant_dict_init (&dict, self->applications);
130 : :
131 [ + + ]: 5 : if (g_variant_lookup (application, "name", "&s", &name))
132 : 4 : g_variant_dict_insert_value (&dict, name, g_variant_ref_sink (application));
133 : 5 : g_variant_unref (application);
134 : :
135 [ + - ]: 5 : g_clear_pointer (&self->applications, g_variant_unref);
136 : 5 : self->applications = g_variant_ref_sink (g_variant_dict_end (&dict));
137 : 5 : }
138 : :
139 : : static void
140 : 6 : on_items_changed (GListModel *list,
141 : : unsigned int position,
142 : : unsigned int removed,
143 : : unsigned int added,
144 : : ValentNotifications *self)
145 : : {
146 [ - + ]: 6 : g_assert (G_IS_LIST_MODEL (list));
147 [ + - ]: 6 : g_assert (VALENT_IS_NOTIFICATIONS (self));
148 : :
149 [ + + ]: 11 : for (unsigned int i = 0; i < added; i++)
150 : : {
151 : 5 : g_autoptr (ValentNotification) notification = NULL;
152 : :
153 : 5 : notification = g_list_model_get_item (list, position + i);
154 [ + - ]: 5 : add_application (self, notification_serialize (notification));
155 : : }
156 : 6 : }
157 : :
158 : : /*
159 : : * ValentComponent
160 : : */
161 : : static void
162 : 5 : valent_notifications_bind_extension (ValentComponent *component,
163 : : ValentExtension *extension)
164 : : {
165 : 5 : ValentNotifications *self = VALENT_NOTIFICATIONS (component);
166 : 5 : ValentNotificationsAdapter *adapter = VALENT_NOTIFICATIONS_ADAPTER (extension);
167 : :
168 : 5 : VALENT_ENTRY;
169 : :
170 [ - + ]: 5 : g_assert (VALENT_IS_NOTIFICATIONS (self));
171 [ + - ]: 5 : g_assert (VALENT_IS_NOTIFICATIONS_ADAPTER (adapter));
172 : :
173 : 5 : g_signal_connect_object (adapter,
174 : : "items-changed",
175 : : G_CALLBACK (on_items_changed),
176 : : self,
177 : : G_CONNECT_DEFAULT);
178 : :
179 : 5 : VALENT_EXIT;
180 : : }
181 : :
182 : : static void
183 : 2 : valent_notifications_unbind_extension (ValentComponent *component,
184 : : ValentExtension *extension)
185 : : {
186 : 2 : ValentNotifications *self = VALENT_NOTIFICATIONS (component);
187 : 2 : ValentNotificationsAdapter *adapter = VALENT_NOTIFICATIONS_ADAPTER (extension);
188 : :
189 [ - + ]: 2 : g_assert (VALENT_IS_NOTIFICATIONS (self));
190 [ + - ]: 2 : g_assert (VALENT_IS_NOTIFICATIONS_ADAPTER (adapter));
191 : :
192 : 2 : g_signal_handlers_disconnect_by_func (adapter, on_items_changed, self);
193 : 2 : }
194 : :
195 : :
196 : : /*
197 : : * GObject
198 : : */
199 : : static void
200 : 0 : valent_notifications_finalize (GObject *object)
201 : : {
202 : 0 : ValentNotifications *self = VALENT_NOTIFICATIONS (object);
203 : :
204 [ # # ]: 0 : g_clear_pointer (&self->applications, g_variant_unref);
205 : :
206 : 0 : G_OBJECT_CLASS (valent_notifications_parent_class)->finalize (object);
207 : 0 : }
208 : :
209 : : static void
210 : 5 : valent_notifications_class_init (ValentNotificationsClass *klass)
211 : : {
212 : 5 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
213 : 5 : ValentComponentClass *component_class = VALENT_COMPONENT_CLASS (klass);
214 : :
215 : 5 : object_class->finalize = valent_notifications_finalize;
216 : :
217 : 5 : component_class->bind_extension = valent_notifications_bind_extension;
218 : 5 : component_class->unbind_extension = valent_notifications_unbind_extension;
219 : : }
220 : :
221 : : static void
222 : 4 : valent_notifications_init (ValentNotifications *self)
223 : : {
224 : 4 : query_applications (self);
225 : 4 : }
226 : :
227 : : /**
228 : : * valent_notifications_get_default:
229 : : *
230 : : * Get the default [class@Valent.Notifications].
231 : : *
232 : : * Returns: (transfer none) (not nullable): a `ValentNotifications`
233 : : *
234 : : * Since: 1.0
235 : : */
236 : : ValentNotifications *
237 : 33 : valent_notifications_get_default (void)
238 : : {
239 : 33 : static ValentNotifications *default_instance = NULL;
240 : :
241 [ + + ]: 33 : if (default_instance == NULL)
242 : : {
243 : 4 : default_instance = g_object_new (VALENT_TYPE_NOTIFICATIONS,
244 : : "plugin-domain", "notifications",
245 : : "plugin-type", VALENT_TYPE_NOTIFICATIONS_ADAPTER,
246 : : NULL);
247 : 4 : g_object_add_weak_pointer (G_OBJECT (default_instance),
248 : : (gpointer)&default_instance);
249 : : }
250 : :
251 : 33 : return default_instance;
252 : : }
253 : :
254 : : /**
255 : : * valent_notifications_get_applications:
256 : : * @notifications: (nullable): a `ValentNotifications`
257 : : *
258 : : * Get a dictionary of applications that are known to send notifications.
259 : : *
260 : : * Returns: (transfer none): a `GVariant`
261 : : *
262 : : * Since: 1.0
263 : : */
264 : : GVariant *
265 : 4 : valent_notifications_get_applications (ValentNotifications *notifications)
266 : : {
267 [ + + - + ]: 4 : g_return_val_if_fail (notifications == NULL || VALENT_IS_NOTIFICATIONS (notifications), NULL);
268 : :
269 [ + + ]: 4 : if (notifications == NULL)
270 : 3 : notifications = valent_notifications_get_default ();
271 : :
272 [ - + ]: 4 : if (notifications->applications == NULL)
273 : 0 : query_applications (notifications);
274 : :
275 : 4 : return notifications->applications;
276 : : }
277 : :
|