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