Coverage Report

Created: 2025-07-01 07:09

/src/glib/gio/gnotification.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright © 2013 Lars Uebernickel
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * This library is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General
15
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
16
 *
17
 * Authors: Lars Uebernickel <lars@uebernic.de>
18
 */
19
20
#include "config.h"
21
22
#include "gnotification-private.h"
23
#include "gdbusutils.h"
24
#include "gicon.h"
25
#include "gaction.h"
26
#include "gioenumtypes.h"
27
28
/**
29
 * SECTION:gnotification
30
 * @short_description: User Notifications (pop up messages)
31
 * @include: gio/gio.h
32
 *
33
 * #GNotification is a mechanism for creating a notification to be shown
34
 * to the user -- typically as a pop-up notification presented by the
35
 * desktop environment shell.
36
 *
37
 * The key difference between #GNotification and other similar APIs is
38
 * that, if supported by the desktop environment, notifications sent
39
 * with #GNotification will persist after the application has exited,
40
 * and even across system reboots.
41
 *
42
 * Since the user may click on a notification while the application is
43
 * not running, applications using #GNotification should be able to be
44
 * started as a D-Bus service, using #GApplication.
45
 *
46
 * User interaction with a notification (either the default action, or
47
 * buttons) must be associated with actions on the application (ie:
48
 * "app." actions).  It is not possible to route user interaction
49
 * through the notification itself, because the object will not exist if
50
 * the application is autostarted as a result of a notification being
51
 * clicked.
52
 *
53
 * A notification can be sent with g_application_send_notification().
54
 *
55
 * Since: 2.40
56
 **/
57
58
/**
59
 * GNotification:
60
 *
61
 * This structure type is private and should only be accessed using the
62
 * public APIs.
63
 *
64
 * Since: 2.40
65
 **/
66
67
typedef GObjectClass GNotificationClass;
68
69
struct _GNotification
70
{
71
  GObject parent;
72
73
  gchar *title;
74
  gchar *body;
75
  GIcon *icon;
76
  GNotificationPriority priority;
77
  GPtrArray *buttons;
78
  gchar *default_action;
79
  GVariant *default_action_target;
80
};
81
82
typedef struct
83
{
84
  gchar *label;
85
  gchar *action_name;
86
  GVariant *target;
87
} Button;
88
89
G_DEFINE_TYPE (GNotification, g_notification, G_TYPE_OBJECT)
90
91
static void
92
button_free (gpointer data)
93
0
{
94
0
  Button *button = data;
95
96
0
  g_free (button->label);
97
0
  g_free (button->action_name);
98
0
  if (button->target)
99
0
    g_variant_unref (button->target);
100
101
0
  g_slice_free (Button, button);
102
0
}
103
104
static void
105
g_notification_dispose (GObject *object)
106
0
{
107
0
  GNotification *notification = G_NOTIFICATION (object);
108
109
0
  g_clear_object (&notification->icon);
110
111
0
  G_OBJECT_CLASS (g_notification_parent_class)->dispose (object);
112
0
}
113
114
static void
115
g_notification_finalize (GObject *object)
116
0
{
117
0
  GNotification *notification = G_NOTIFICATION (object);
118
119
0
  g_free (notification->title);
120
0
  g_free (notification->body);
121
0
  g_free (notification->default_action);
122
0
  if (notification->default_action_target)
123
0
    g_variant_unref (notification->default_action_target);
124
0
  g_ptr_array_free (notification->buttons, TRUE);
125
126
0
  G_OBJECT_CLASS (g_notification_parent_class)->finalize (object);
127
0
}
128
129
static void
130
g_notification_class_init (GNotificationClass *klass)
131
0
{
132
0
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
133
134
0
  object_class->dispose = g_notification_dispose;
135
0
  object_class->finalize = g_notification_finalize;
136
0
}
137
138
static void
139
g_notification_init (GNotification *notification)
140
0
{
141
0
  notification->buttons = g_ptr_array_new_full (2, button_free);
142
0
}
143
144
/**
145
 * g_notification_new:
146
 * @title: the title of the notification
147
 *
148
 * Creates a new #GNotification with @title as its title.
149
 *
150
 * After populating @notification with more details, it can be sent to
151
 * the desktop shell with g_application_send_notification(). Changing
152
 * any properties after this call will not have any effect until
153
 * resending @notification.
154
 *
155
 * Returns: a new #GNotification instance
156
 *
157
 * Since: 2.40
158
 */
159
GNotification *
160
g_notification_new (const gchar *title)
161
0
{
162
0
  GNotification *notification;
163
164
0
  g_return_val_if_fail (title != NULL, NULL);
165
166
0
  notification = g_object_new (G_TYPE_NOTIFICATION, NULL);
167
0
  notification->title = g_strdup (title);
168
169
0
  return notification;
170
0
}
171
172
/*< private >
173
 * g_notification_get_title:
174
 * @notification: a #GNotification
175
 *
176
 * Gets the title of @notification.
177
 *
178
 * Returns: the title of @notification
179
 *
180
 * Since: 2.40
181
 */
182
const gchar *
183
g_notification_get_title (GNotification *notification)
184
0
{
185
0
  g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL);
186
187
0
  return notification->title;
188
0
}
189
190
/**
191
 * g_notification_set_title:
192
 * @notification: a #GNotification
193
 * @title: the new title for @notification
194
 *
195
 * Sets the title of @notification to @title.
196
 *
197
 * Since: 2.40
198
 */
199
void
200
g_notification_set_title (GNotification *notification,
201
                          const gchar   *title)
202
0
{
203
0
  g_return_if_fail (G_IS_NOTIFICATION (notification));
204
0
  g_return_if_fail (title != NULL);
205
206
0
  g_free (notification->title);
207
208
0
  notification->title = g_strdup (title);
209
0
}
210
211
/*< private >
212
 * g_notification_get_body:
213
 * @notification: a #GNotification
214
 *
215
 * Gets the current body of @notification.
216
 *
217
 * Returns: (nullable): the body of @notification
218
 *
219
 * Since: 2.40
220
 */
221
const gchar *
222
g_notification_get_body (GNotification *notification)
223
0
{
224
0
  g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL);
225
226
0
  return notification->body;
227
0
}
228
229
/**
230
 * g_notification_set_body:
231
 * @notification: a #GNotification
232
 * @body: (nullable): the new body for @notification, or %NULL
233
 *
234
 * Sets the body of @notification to @body.
235
 *
236
 * Since: 2.40
237
 */
238
void
239
g_notification_set_body (GNotification *notification,
240
                         const gchar   *body)
241
0
{
242
0
  g_return_if_fail (G_IS_NOTIFICATION (notification));
243
0
  g_return_if_fail (body != NULL);
244
245
0
  g_free (notification->body);
246
247
0
  notification->body = g_strdup (body);
248
0
}
249
250
/*< private >
251
 * g_notification_get_icon:
252
 * @notification: a #GNotification
253
 *
254
 * Gets the icon currently set on @notification.
255
 *
256
 * Returns: (transfer none): the icon associated with @notification
257
 *
258
 * Since: 2.40
259
 */
260
GIcon *
261
g_notification_get_icon (GNotification *notification)
262
0
{
263
0
  g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL);
264
265
0
  return notification->icon;
266
0
}
267
268
/**
269
 * g_notification_set_icon:
270
 * @notification: a #GNotification
271
 * @icon: the icon to be shown in @notification, as a #GIcon
272
 *
273
 * Sets the icon of @notification to @icon.
274
 *
275
 * Since: 2.40
276
 */
277
void
278
g_notification_set_icon (GNotification *notification,
279
                         GIcon         *icon)
280
0
{
281
0
  g_return_if_fail (G_IS_NOTIFICATION (notification));
282
283
0
  if (notification->icon)
284
0
    g_object_unref (notification->icon);
285
286
0
  notification->icon = g_object_ref (icon);
287
0
}
288
289
/*< private >
290
 * g_notification_get_priority:
291
 * @notification: a #GNotification
292
 *
293
 * Returns the priority of @notification
294
 *
295
 * Since: 2.42
296
 */
297
GNotificationPriority
298
g_notification_get_priority (GNotification *notification)
299
0
{
300
0
  g_return_val_if_fail (G_IS_NOTIFICATION (notification), G_NOTIFICATION_PRIORITY_NORMAL);
301
302
0
  return notification->priority;
303
0
}
304
305
/**
306
 * g_notification_set_urgent:
307
 * @notification: a #GNotification
308
 * @urgent: %TRUE if @notification is urgent
309
 *
310
 * Deprecated in favor of g_notification_set_priority().
311
 *
312
 * Since: 2.40
313
 * Deprecated: 2.42: Since 2.42, this has been deprecated in favour of
314
 *    g_notification_set_priority().
315
 */
316
void
317
g_notification_set_urgent (GNotification *notification,
318
                           gboolean       urgent)
319
0
{
320
0
  g_return_if_fail (G_IS_NOTIFICATION (notification));
321
322
0
  notification->priority = urgent ?
323
0
      G_NOTIFICATION_PRIORITY_URGENT :
324
0
      G_NOTIFICATION_PRIORITY_NORMAL;
325
0
}
326
327
/**
328
 * g_notification_set_priority:
329
 * @notification: a #GNotification
330
 * @priority: a #GNotificationPriority
331
 *
332
 * Sets the priority of @notification to @priority. See
333
 * #GNotificationPriority for possible values.
334
 */
335
void
336
g_notification_set_priority (GNotification         *notification,
337
                             GNotificationPriority  priority)
338
0
{
339
0
  g_return_if_fail (G_IS_NOTIFICATION (notification));
340
341
0
  notification->priority = priority;
342
0
}
343
344
/**
345
 * g_notification_add_button:
346
 * @notification: a #GNotification
347
 * @label: label of the button
348
 * @detailed_action: a detailed action name
349
 *
350
 * Adds a button to @notification that activates the action in
351
 * @detailed_action when clicked. That action must be an
352
 * application-wide action (starting with "app."). If @detailed_action
353
 * contains a target, the action will be activated with that target as
354
 * its parameter.
355
 *
356
 * See g_action_parse_detailed_name() for a description of the format
357
 * for @detailed_action.
358
 *
359
 * Since: 2.40
360
 */
361
void
362
g_notification_add_button (GNotification *notification,
363
                           const gchar   *label,
364
                           const gchar   *detailed_action)
365
0
{
366
0
  gchar *action;
367
0
  GVariant *target;
368
0
  GError *error = NULL;
369
370
0
  g_return_if_fail (detailed_action != NULL);
371
372
0
  if (!g_action_parse_detailed_name (detailed_action, &action, &target, &error))
373
0
    {
374
0
      g_warning ("%s: %s", G_STRFUNC, error->message);
375
0
      g_error_free (error);
376
0
      return;
377
0
    }
378
379
0
  g_notification_add_button_with_target_value (notification, label, action, target);
380
381
0
  g_free (action);
382
0
  if (target)
383
0
    g_variant_unref (target);
384
0
}
385
386
/**
387
 * g_notification_add_button_with_target: (skip)
388
 * @notification: a #GNotification
389
 * @label: label of the button
390
 * @action: an action name
391
 * @target_format: (nullable): a #GVariant format string, or %NULL
392
 * @...: positional parameters, as determined by @target_format
393
 *
394
 * Adds a button to @notification that activates @action when clicked.
395
 * @action must be an application-wide action (it must start with "app.").
396
 *
397
 * If @target_format is given, it is used to collect remaining
398
 * positional parameters into a #GVariant instance, similar to
399
 * g_variant_new(). @action will be activated with that #GVariant as its
400
 * parameter.
401
 *
402
 * Since: 2.40
403
 */
404
void
405
g_notification_add_button_with_target (GNotification *notification,
406
                                       const gchar   *label,
407
                                       const gchar   *action,
408
                                       const gchar   *target_format,
409
                                       ...)
410
0
{
411
0
  va_list args;
412
0
  GVariant *target = NULL;
413
414
0
  if (target_format)
415
0
    {
416
0
      va_start (args, target_format);
417
0
      target = g_variant_new_va (target_format, NULL, &args);
418
0
      va_end (args);
419
0
    }
420
421
0
  g_notification_add_button_with_target_value (notification, label, action, target);
422
0
}
423
424
/**
425
 * g_notification_add_button_with_target_value: (rename-to g_notification_add_button_with_target)
426
 * @notification: a #GNotification
427
 * @label: label of the button
428
 * @action: an action name
429
 * @target: (nullable): a #GVariant to use as @action's parameter, or %NULL
430
 *
431
 * Adds a button to @notification that activates @action when clicked.
432
 * @action must be an application-wide action (it must start with "app.").
433
 *
434
 * If @target is non-%NULL, @action will be activated with @target as
435
 * its parameter.
436
 *
437
 * Since: 2.40
438
 */
439
void
440
g_notification_add_button_with_target_value (GNotification *notification,
441
                                             const gchar   *label,
442
                                             const gchar   *action,
443
                                             GVariant      *target)
444
0
{
445
0
  Button *button;
446
447
0
  g_return_if_fail (G_IS_NOTIFICATION (notification));
448
0
  g_return_if_fail (label != NULL);
449
0
  g_return_if_fail (action != NULL && g_action_name_is_valid (action));
450
451
0
  if (!g_str_has_prefix (action, "app."))
452
0
    {
453
0
      g_warning ("%s: action '%s' does not start with 'app.'."
454
0
                 "This is unlikely to work properly.", G_STRFUNC, action);
455
0
    }
456
457
0
  button =  g_slice_new0 (Button);
458
0
  button->label = g_strdup (label);
459
0
  button->action_name = g_strdup (action);
460
461
0
  if (target)
462
0
    button->target = g_variant_ref_sink (target);
463
464
0
  g_ptr_array_add (notification->buttons, button);
465
0
}
466
467
/*< private >
468
 * g_notification_get_n_buttons:
469
 * @notification: a #GNotification
470
 *
471
 * Returns: the amount of buttons added to @notification.
472
 */
473
guint
474
g_notification_get_n_buttons (GNotification *notification)
475
0
{
476
0
  return notification->buttons->len;
477
0
}
478
479
/*< private >
480
 * g_notification_get_button:
481
 * @notification: a #GNotification
482
 * @index: index of the button
483
 * @label: (): return location for the button's label
484
 * @action: (): return location for the button's associated action
485
 * @target: (): return location for the target @action should be
486
 * activated with
487
 *
488
 * Returns a description of a button that was added to @notification
489
 * with g_notification_add_button().
490
 *
491
 * @index must be smaller than the value returned by
492
 * g_notification_get_n_buttons().
493
 */
494
void
495
g_notification_get_button (GNotification  *notification,
496
                           gint            index,
497
                           gchar         **label,
498
                           gchar         **action,
499
                           GVariant      **target)
500
0
{
501
0
  Button *button;
502
503
0
  button = g_ptr_array_index (notification->buttons, index);
504
505
0
  if (label)
506
0
    *label = g_strdup (button->label);
507
508
0
  if (action)
509
0
    *action = g_strdup (button->action_name);
510
511
0
  if (target)
512
0
    *target = button->target ? g_variant_ref (button->target) : NULL;
513
0
}
514
515
/*< private >
516
 * g_notification_get_button_with_action:
517
 * @notification: a #GNotification
518
 * @action: an action name
519
 *
520
 * Returns the index of the button in @notification that is associated
521
 * with @action, or -1 if no such button exists.
522
 */
523
gint
524
g_notification_get_button_with_action (GNotification *notification,
525
                                       const gchar   *action)
526
0
{
527
0
  guint i;
528
529
0
  for (i = 0; i < notification->buttons->len; i++)
530
0
    {
531
0
      Button *button;
532
533
0
      button = g_ptr_array_index (notification->buttons, i);
534
0
      if (g_str_equal (action, button->action_name))
535
0
        return i;
536
0
    }
537
538
0
  return -1;
539
0
}
540
541
542
/*< private >
543
 * g_notification_get_default_action:
544
 * @notification: a #GNotification
545
 * @action: (nullable): return location for the default action
546
 * @target: (nullable): return location for the target of the default action
547
 *
548
 * Gets the action and target for the default action of @notification.
549
 *
550
 * Returns: %TRUE if @notification has a default action
551
 */
552
gboolean
553
g_notification_get_default_action (GNotification  *notification,
554
                                   gchar         **action,
555
                                   GVariant      **target)
556
0
{
557
0
  if (notification->default_action == NULL)
558
0
    return FALSE;
559
560
0
  if (action)
561
0
    *action = g_strdup (notification->default_action);
562
563
0
  if (target)
564
0
    {
565
0
      if (notification->default_action_target)
566
0
        *target = g_variant_ref (notification->default_action_target);
567
0
      else
568
0
        *target = NULL;
569
0
    }
570
571
0
  return TRUE;
572
0
}
573
574
/**
575
 * g_notification_set_default_action:
576
 * @notification: a #GNotification
577
 * @detailed_action: a detailed action name
578
 *
579
 * Sets the default action of @notification to @detailed_action. This
580
 * action is activated when the notification is clicked on.
581
 *
582
 * The action in @detailed_action must be an application-wide action (it
583
 * must start with "app."). If @detailed_action contains a target, the
584
 * given action will be activated with that target as its parameter.
585
 * See g_action_parse_detailed_name() for a description of the format
586
 * for @detailed_action.
587
 *
588
 * When no default action is set, the application that the notification
589
 * was sent on is activated.
590
 *
591
 * Since: 2.40
592
 */
593
void
594
g_notification_set_default_action (GNotification *notification,
595
                                   const gchar   *detailed_action)
596
0
{
597
0
  gchar *action;
598
0
  GVariant *target;
599
0
  GError *error = NULL;
600
601
0
  if (!g_action_parse_detailed_name (detailed_action, &action, &target, &error))
602
0
    {
603
0
      g_warning ("%s: %s", G_STRFUNC, error->message);
604
0
      g_error_free (error);
605
0
      return;
606
0
    }
607
608
0
  g_notification_set_default_action_and_target_value (notification, action, target);
609
610
0
  g_free (action);
611
0
  if (target)
612
0
    g_variant_unref (target);
613
0
}
614
615
/**
616
 * g_notification_set_default_action_and_target: (skip)
617
 * @notification: a #GNotification
618
 * @action: an action name
619
 * @target_format: (nullable): a #GVariant format string, or %NULL
620
 * @...: positional parameters, as determined by @target_format
621
 *
622
 * Sets the default action of @notification to @action. This action is
623
 * activated when the notification is clicked on. It must be an
624
 * application-wide action (it must start with "app.").
625
 *
626
 * If @target_format is given, it is used to collect remaining
627
 * positional parameters into a #GVariant instance, similar to
628
 * g_variant_new(). @action will be activated with that #GVariant as its
629
 * parameter.
630
 *
631
 * When no default action is set, the application that the notification
632
 * was sent on is activated.
633
 *
634
 * Since: 2.40
635
 */
636
void
637
g_notification_set_default_action_and_target (GNotification *notification,
638
                                              const gchar   *action,
639
                                              const gchar   *target_format,
640
                                              ...)
641
0
{
642
0
  va_list args;
643
0
  GVariant *target = NULL;
644
645
0
  if (target_format)
646
0
    {
647
0
      va_start (args, target_format);
648
0
      target = g_variant_new_va (target_format, NULL, &args);
649
0
      va_end (args);
650
0
    }
651
652
0
  g_notification_set_default_action_and_target_value (notification, action, target);
653
0
}
654
655
/**
656
 * g_notification_set_default_action_and_target_value: (rename-to g_notification_set_default_action_and_target)
657
 * @notification: a #GNotification
658
 * @action: an action name
659
 * @target: (nullable): a #GVariant to use as @action's parameter, or %NULL
660
 *
661
 * Sets the default action of @notification to @action. This action is
662
 * activated when the notification is clicked on. It must be an
663
 * application-wide action (start with "app.").
664
 *
665
 * If @target is non-%NULL, @action will be activated with @target as
666
 * its parameter.
667
 *
668
 * When no default action is set, the application that the notification
669
 * was sent on is activated.
670
 *
671
 * Since: 2.40
672
 */
673
void
674
g_notification_set_default_action_and_target_value (GNotification *notification,
675
                                                    const gchar   *action,
676
                                                    GVariant      *target)
677
0
{
678
0
  g_return_if_fail (G_IS_NOTIFICATION (notification));
679
0
  g_return_if_fail (action != NULL && g_action_name_is_valid (action));
680
681
0
  if (!g_str_has_prefix (action, "app."))
682
0
    {
683
0
      g_warning ("%s: action '%s' does not start with 'app.'."
684
0
                 "This is unlikely to work properly.", G_STRFUNC, action);
685
0
    }
686
687
0
  g_free (notification->default_action);
688
0
  g_clear_pointer (&notification->default_action_target, g_variant_unref);
689
690
0
  notification->default_action = g_strdup (action);
691
692
0
  if (target)
693
0
    notification->default_action_target = g_variant_ref_sink (target);
694
0
}
695
696
static GVariant *
697
g_notification_serialize_button (Button *button)
698
0
{
699
0
  GVariantBuilder builder;
700
701
0
  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
702
703
0
  g_variant_builder_add (&builder, "{sv}", "label", g_variant_new_string (button->label));
704
0
  g_variant_builder_add (&builder, "{sv}", "action", g_variant_new_string (button->action_name));
705
706
0
  if (button->target)
707
0
    g_variant_builder_add (&builder, "{sv}", "target", button->target);
708
709
0
  return g_variant_builder_end (&builder);
710
0
}
711
712
static GVariant *
713
g_notification_get_priority_nick (GNotification *notification)
714
0
{
715
0
  GEnumClass *enum_class;
716
0
  GEnumValue *value;
717
0
  GVariant *nick;
718
719
0
  enum_class = g_type_class_ref (G_TYPE_NOTIFICATION_PRIORITY);
720
0
  value = g_enum_get_value (enum_class, g_notification_get_priority (notification));
721
0
  g_assert (value != NULL);
722
0
  nick = g_variant_new_string (value->value_nick);
723
0
  g_type_class_unref (enum_class);
724
725
0
  return nick;
726
0
}
727
728
/*< private >
729
 * g_notification_serialize:
730
 *
731
 * Serializes @notification into a floating variant of type a{sv}.
732
 *
733
 * Returns: the serialized @notification as a floating variant.
734
 */
735
GVariant *
736
g_notification_serialize (GNotification *notification)
737
0
{
738
0
  GVariantBuilder builder;
739
740
0
  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
741
742
0
  if (notification->title)
743
0
    g_variant_builder_add (&builder, "{sv}", "title", g_variant_new_string (notification->title));
744
745
0
  if (notification->body)
746
0
    g_variant_builder_add (&builder, "{sv}", "body", g_variant_new_string (notification->body));
747
748
0
  if (notification->icon)
749
0
    {
750
0
      GVariant *serialized_icon;
751
752
0
      if ((serialized_icon = g_icon_serialize (notification->icon)))
753
0
        {
754
0
          g_variant_builder_add (&builder, "{sv}", "icon", serialized_icon);
755
0
          g_variant_unref (serialized_icon);
756
0
        }
757
0
    }
758
759
0
  g_variant_builder_add (&builder, "{sv}", "priority", g_notification_get_priority_nick (notification));
760
761
0
  if (notification->default_action)
762
0
    {
763
0
      g_variant_builder_add (&builder, "{sv}", "default-action",
764
0
                                               g_variant_new_string (notification->default_action));
765
766
0
      if (notification->default_action_target)
767
0
        g_variant_builder_add (&builder, "{sv}", "default-action-target",
768
0
                                                  notification->default_action_target);
769
0
    }
770
771
0
  if (notification->buttons->len > 0)
772
0
    {
773
0
      GVariantBuilder actions_builder;
774
0
      guint i;
775
776
0
      g_variant_builder_init (&actions_builder, G_VARIANT_TYPE ("aa{sv}"));
777
778
0
      for (i = 0; i < notification->buttons->len; i++)
779
0
        {
780
0
          Button *button = g_ptr_array_index (notification->buttons, i);
781
0
          g_variant_builder_add (&actions_builder, "@a{sv}", g_notification_serialize_button (button));
782
0
        }
783
784
0
      g_variant_builder_add (&builder, "{sv}", "buttons", g_variant_builder_end (&actions_builder));
785
0
    }
786
787
0
  return g_variant_builder_end (&builder);
788
0
}