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 : :
32 : : struct _ValentNotifications
33 : : {
34 : : ValentComponent parent_instance;
35 : :
36 : : GVariant *applications;
37 : : };
38 : :
39 [ + + + - ]: 191 : G_DEFINE_FINAL_TYPE (ValentNotifications, valent_notifications, VALENT_TYPE_COMPONENT)
40 : :
41 : : enum {
42 : : NOTIFICATION_ADDED,
43 : : NOTIFICATION_REMOVED,
44 : : N_SIGNALS
45 : : };
46 : :
47 : : static guint signals[N_SIGNALS] = { 0, };
48 : :
49 : :
50 : : static ValentNotifications *default_listener = NULL;
51 : :
52 : :
53 : : static GVariant *
54 : 0 : app_info_serialize (GAppInfo *info)
55 : : {
56 : 0 : GVariantDict dict;
57 : 0 : const char *name;
58 : 0 : GIcon *icon;
59 : :
60 [ # # # # : 0 : g_assert (G_IS_APP_INFO (info));
# # # # ]
61 : :
62 : 0 : g_variant_dict_init (&dict, NULL);
63 : :
64 [ # # ]: 0 : if ((name = g_app_info_get_display_name (info)) != NULL)
65 : 0 : g_variant_dict_insert (&dict, "name", "s", name);
66 : :
67 [ # # ]: 0 : if ((icon = g_app_info_get_icon (info)) != NULL)
68 : : {
69 : 0 : g_autoptr (GVariant) iconv = NULL;
70 : :
71 : 0 : iconv = g_icon_serialize (icon);
72 [ # # ]: 0 : g_variant_dict_insert_value (&dict, "icon", iconv);
73 : : }
74 : :
75 : 0 : return g_variant_dict_end (&dict);
76 : : }
77 : :
78 : : static GVariant *
79 : 11 : notification_serialize (ValentNotification *notification)
80 : : {
81 : 11 : GVariantDict dict;
82 : 11 : const char *app_name;
83 : 11 : GIcon *icon;
84 : :
85 : 11 : g_variant_dict_init (&dict, NULL);
86 : :
87 [ + + ]: 11 : if ((app_name = valent_notification_get_application (notification)) != NULL)
88 : 7 : g_variant_dict_insert (&dict, "name", "s", app_name);
89 : :
90 [ + + ]: 11 : if ((icon = valent_notification_get_icon (notification)) != NULL)
91 : : {
92 : 18 : g_autoptr (GVariant) iconv = NULL;
93 : :
94 : 7 : iconv = g_icon_serialize (icon);
95 [ + - ]: 7 : g_variant_dict_insert_value (&dict, "icon", iconv);
96 : : }
97 : :
98 : 11 : return g_variant_dict_end (&dict);
99 : : }
100 : :
101 : : static void
102 : 7 : query_applications (ValentNotifications *self)
103 : : {
104 : 7 : GVariantDict dict;
105 : 14 : g_autolist (GAppInfo) infos = NULL;
106 : :
107 [ + - ]: 7 : g_assert (VALENT_IS_NOTIFICATIONS (self));
108 : :
109 : 7 : g_variant_dict_init (&dict, NULL);
110 : 7 : infos = g_app_info_get_all ();
111 : :
112 [ - + ]: 7 : for (const GList *iter = infos; iter; iter = iter->next)
113 : : {
114 : 0 : const char *desktop_id;
115 : 0 : const char *name;
116 : :
117 : 0 : desktop_id = g_app_info_get_id (iter->data);
118 : :
119 [ # # # # : 0 : if G_UNLIKELY (g_str_has_prefix (desktop_id, APPLICATION_ID))
# # ]
120 : 0 : continue;
121 : :
122 [ # # ]: 0 : if (!g_desktop_app_info_get_boolean (iter->data, "X-GNOME-UsesNotifications"))
123 : 0 : continue;
124 : :
125 : 0 : name = g_app_info_get_display_name (iter->data);
126 : 0 : g_variant_dict_insert_value (&dict, name, app_info_serialize (iter->data));
127 : : }
128 : :
129 : 7 : self->applications = g_variant_ref_sink (g_variant_dict_end (&dict));
130 : 7 : }
131 : :
132 : : static void
133 : 11 : add_application (ValentNotifications *self,
134 : : GVariant *application)
135 : : {
136 : 11 : GVariantDict dict;
137 : 11 : const char *name;
138 : :
139 [ - + ]: 11 : if (self->applications == NULL)
140 : 0 : query_applications (self);
141 : :
142 : 11 : g_variant_dict_init (&dict, self->applications);
143 : :
144 [ + + ]: 11 : if (g_variant_lookup (application, "name", "&s", &name))
145 : 7 : g_variant_dict_insert_value (&dict, name, g_variant_ref_sink (application));
146 : 11 : g_variant_unref (application);
147 : :
148 [ + - ]: 11 : g_clear_pointer (&self->applications, g_variant_unref);
149 : 11 : self->applications = g_variant_ref_sink (g_variant_dict_end (&dict));
150 : 11 : }
151 : :
152 : :
153 : : /*
154 : : * ValentNotificationsAdapter Callbacks
155 : : */
156 : : static void
157 : 11 : on_notification_added (ValentNotificationsAdapter *adapter,
158 : : ValentNotification *notification,
159 : : ValentNotifications *self)
160 : : {
161 [ + - ]: 11 : g_assert (VALENT_IS_NOTIFICATIONS_ADAPTER (adapter));
162 [ - + ]: 11 : g_assert (VALENT_IS_NOTIFICATION (notification));
163 [ - + ]: 11 : g_assert (VALENT_IS_NOTIFICATIONS (self));
164 : :
165 : 11 : add_application (self, notification_serialize (notification));
166 : :
167 : 11 : g_signal_emit (G_OBJECT (self), signals [NOTIFICATION_ADDED], 0, notification);
168 : 11 : }
169 : :
170 : : static void
171 : 6 : on_notification_removed (ValentNotificationsAdapter *adapter,
172 : : const char *id,
173 : : ValentNotifications *self)
174 : : {
175 [ + - ]: 6 : g_assert (VALENT_IS_NOTIFICATIONS_ADAPTER (adapter));
176 [ - + ]: 6 : g_assert (id != NULL);
177 [ - + ]: 6 : g_assert (VALENT_IS_NOTIFICATIONS (self));
178 : :
179 : 6 : g_signal_emit (G_OBJECT (self), signals [NOTIFICATION_REMOVED], 0, id);
180 : 6 : }
181 : :
182 : :
183 : : /*
184 : : * ValentComponent
185 : : */
186 : : static void
187 : 7 : valent_notifications_bind_extension (ValentComponent *component,
188 : : GObject *extension)
189 : : {
190 : 7 : ValentNotifications *self = VALENT_NOTIFICATIONS (component);
191 : 7 : ValentNotificationsAdapter *adapter = VALENT_NOTIFICATIONS_ADAPTER (extension);
192 : :
193 : 7 : VALENT_ENTRY;
194 : :
195 [ + - ]: 7 : g_assert (VALENT_IS_NOTIFICATIONS (self));
196 [ - + ]: 7 : g_assert (VALENT_IS_NOTIFICATIONS_ADAPTER (adapter));
197 : :
198 : 7 : g_signal_connect_object (adapter,
199 : : "notification-added",
200 : : G_CALLBACK (on_notification_added),
201 : : self, 0);
202 : :
203 : 7 : g_signal_connect_object (adapter,
204 : : "notification-removed",
205 : : G_CALLBACK (on_notification_removed),
206 : : self, 0);
207 : :
208 : 7 : VALENT_EXIT;
209 : : }
210 : :
211 : : static void
212 : 5 : valent_notifications_unbind_extension (ValentComponent *component,
213 : : GObject *extension)
214 : : {
215 : 5 : ValentNotifications *self = VALENT_NOTIFICATIONS (component);
216 : 5 : ValentNotificationsAdapter *adapter = VALENT_NOTIFICATIONS_ADAPTER (extension);
217 : :
218 [ + - ]: 5 : g_assert (VALENT_IS_NOTIFICATIONS (self));
219 [ - + ]: 5 : g_assert (VALENT_IS_NOTIFICATIONS_ADAPTER (adapter));
220 : :
221 : 5 : g_signal_handlers_disconnect_by_func (adapter, on_notification_added, self);
222 : 5 : g_signal_handlers_disconnect_by_func (adapter, on_notification_removed, self);
223 : 5 : }
224 : :
225 : :
226 : : /*
227 : : * GObject
228 : : */
229 : : static void
230 : 5 : valent_notifications_finalize (GObject *object)
231 : : {
232 : 5 : ValentNotifications *self = VALENT_NOTIFICATIONS (object);
233 : :
234 [ + - ]: 5 : g_clear_pointer (&self->applications, g_variant_unref);
235 : :
236 : 5 : G_OBJECT_CLASS (valent_notifications_parent_class)->finalize (object);
237 : 5 : }
238 : :
239 : : static void
240 : 6 : valent_notifications_class_init (ValentNotificationsClass *klass)
241 : : {
242 : 6 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
243 : 6 : ValentComponentClass *component_class = VALENT_COMPONENT_CLASS (klass);
244 : :
245 : 6 : object_class->finalize = valent_notifications_finalize;
246 : :
247 : 6 : component_class->bind_extension = valent_notifications_bind_extension;
248 : 6 : component_class->unbind_extension = valent_notifications_unbind_extension;
249 : :
250 : : /**
251 : : * ValentNotifications::notification-added:
252 : : * @notifications: a `ValentNotifications`
253 : : * @notification: a `ValentNotification`
254 : : *
255 : : * Emitted when a notification is added to a
256 : : * [class@Valent.NotificationsAdapter].
257 : : *
258 : : * Since: 1.0
259 : : */
260 : 12 : signals [NOTIFICATION_ADDED] =
261 : 6 : g_signal_new ("notification-added",
262 : : G_TYPE_FROM_CLASS (klass),
263 : : G_SIGNAL_RUN_FIRST,
264 : : 0,
265 : : NULL, NULL,
266 : : g_cclosure_marshal_VOID__OBJECT,
267 : : G_TYPE_NONE, 1, VALENT_TYPE_NOTIFICATION);
268 : 6 : g_signal_set_va_marshaller (signals [NOTIFICATION_ADDED],
269 : : G_TYPE_FROM_CLASS (klass),
270 : : g_cclosure_marshal_VOID__OBJECTv);
271 : :
272 : : /**
273 : : * ValentNotifications::notification-removed:
274 : : * @notifications: a `ValentNotifications`
275 : : * @id: a notification id
276 : : *
277 : : * Emitted when a notification is removed from a
278 : : * [class@Valent.NotificationsAdapter].
279 : : *
280 : : * Since: 1.0
281 : : */
282 : 12 : signals [NOTIFICATION_REMOVED] =
283 : 6 : g_signal_new ("notification-removed",
284 : : G_TYPE_FROM_CLASS (klass),
285 : : G_SIGNAL_RUN_FIRST,
286 : : 0,
287 : : NULL, NULL,
288 : : g_cclosure_marshal_VOID__STRING,
289 : : G_TYPE_NONE, 1, G_TYPE_STRING);
290 : 6 : g_signal_set_va_marshaller (signals [NOTIFICATION_REMOVED],
291 : : G_TYPE_FROM_CLASS (klass),
292 : : g_cclosure_marshal_VOID__STRINGv);
293 : 6 : }
294 : :
295 : : static void
296 : 7 : valent_notifications_init (ValentNotifications *self)
297 : : {
298 : 7 : query_applications (self);
299 : 7 : }
300 : :
301 : : /**
302 : : * valent_notifications_get_default:
303 : : *
304 : : * Get the default [class@Valent.Notifications].
305 : : *
306 : : * Returns: (transfer none) (not nullable): a `ValentNotifications`
307 : : *
308 : : * Since: 1.0
309 : : */
310 : : ValentNotifications *
311 : 45 : valent_notifications_get_default (void)
312 : : {
313 [ + + ]: 45 : if (default_listener == NULL)
314 : : {
315 : 7 : default_listener = g_object_new (VALENT_TYPE_NOTIFICATIONS,
316 : : "plugin-domain", "notifications",
317 : : "plugin-type", VALENT_TYPE_NOTIFICATIONS_ADAPTER,
318 : : NULL);
319 : :
320 : 7 : g_object_add_weak_pointer (G_OBJECT (default_listener),
321 : : (gpointer)&default_listener);
322 : : }
323 : :
324 : 45 : return default_listener;
325 : : }
326 : :
327 : : /**
328 : : * valent_notifications_get_applications:
329 : : * @notifications: (nullable): a `ValentNotifications`
330 : : *
331 : : * Get a dictionary of applications that are known to send notifications.
332 : : *
333 : : * Returns: (transfer none): a `GVariant`
334 : : *
335 : : * Since: 1.0
336 : : */
337 : : GVariant *
338 : 1 : valent_notifications_get_applications (ValentNotifications *notifications)
339 : : {
340 [ - + - - ]: 1 : g_return_val_if_fail (notifications == NULL || VALENT_IS_NOTIFICATIONS (notifications), NULL);
341 : :
342 [ + - ]: 1 : if (notifications == NULL)
343 : 1 : notifications = valent_notifications_get_default ();
344 : :
345 [ - + ]: 1 : if (notifications->applications == NULL)
346 : 0 : query_applications (notifications);
347 : :
348 : 1 : return notifications->applications;
349 : : }
350 : :
|