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 * In order for #GNotification to work, the application must have installed 47 * a `.desktop` file. For example: 48 * |[ 49 * [Desktop Entry] 50 * Name=Test Application 51 * Comment=Description of what Test Application does 52 * Exec=gnome-test-application 53 * Icon=org.gnome.TestApplication 54 * Terminal=false 55 * Type=Application 56 * Categories=GNOME;GTK;TestApplication Category; 57 * StartupNotify=true 58 * DBusActivatable=true 59 * X-GNOME-UsesNotifications=true 60 * ]| 61 * 62 * The `X-GNOME-UsesNotifications` key indicates to GNOME Control Center 63 * that this application uses notifications, so it can be listed in the 64 * Control Center’s ‘Notifications’ panel. 65 * 66 * The `.desktop` file must be named as `org.gnome.TestApplication.desktop`, 67 * where `org.gnome.TestApplication` is the ID passed to g_application_new(). 68 * 69 * User interaction with a notification (either the default action, or 70 * buttons) must be associated with actions on the application (ie: 71 * "app." actions). It is not possible to route user interaction 72 * through the notification itself, because the object will not exist if 73 * the application is autostarted as a result of a notification being 74 * clicked. 75 * 76 * A notification can be sent with g_application_send_notification(). 77 * 78 * Since: 2.40 79 **/ 80 81 /** 82 * GNotification: 83 * 84 * This structure type is private and should only be accessed using the 85 * public APIs. 86 * 87 * Since: 2.40 88 **/ 89 90 typedef GObjectClass GNotificationClass; 91 92 struct _GNotification 93 { 94 GObject parent; 95 96 gchar *title; 97 gchar *body; 98 GIcon *icon; 99 GNotificationPriority priority; 100 gchar *category; 101 GPtrArray *buttons; 102 gchar *default_action; 103 GVariant *default_action_target; 104 }; 105 106 typedef struct 107 { 108 gchar *label; 109 gchar *action_name; 110 GVariant *target; 111 } Button; 112 113 G_DEFINE_TYPE (GNotification, g_notification, G_TYPE_OBJECT) 114 115 static void 116 button_free (gpointer data) 117 { 118 Button *button = data; 119 120 g_free (button->label); 121 g_free (button->action_name); 122 if (button->target) 123 g_variant_unref (button->target); 124 125 g_slice_free (Button, button); 126 } 127 128 static void 129 g_notification_dispose (GObject *object) 130 { 131 GNotification *notification = G_NOTIFICATION (object); 132 133 g_clear_object (¬ification->icon); 134 135 G_OBJECT_CLASS (g_notification_parent_class)->dispose (object); 136 } 137 138 static void 139 g_notification_finalize (GObject *object) 140 { 141 GNotification *notification = G_NOTIFICATION (object); 142 143 g_free (notification->title); 144 g_free (notification->body); 145 g_free (notification->category); 146 g_free (notification->default_action); 147 if (notification->default_action_target) 148 g_variant_unref (notification->default_action_target); 149 g_ptr_array_free (notification->buttons, TRUE); 150 151 G_OBJECT_CLASS (g_notification_parent_class)->finalize (object); 152 } 153 154 static void 155 g_notification_class_init (GNotificationClass *klass) 156 { 157 GObjectClass *object_class = G_OBJECT_CLASS (klass); 158 159 object_class->dispose = g_notification_dispose; 160 object_class->finalize = g_notification_finalize; 161 } 162 163 static void 164 g_notification_init (GNotification *notification) 165 { 166 notification->buttons = g_ptr_array_new_full (2, button_free); 167 } 168 169 /** 170 * g_notification_new: 171 * @title: the title of the notification 172 * 173 * Creates a new #GNotification with @title as its title. 174 * 175 * After populating @notification with more details, it can be sent to 176 * the desktop shell with g_application_send_notification(). Changing 177 * any properties after this call will not have any effect until 178 * resending @notification. 179 * 180 * Returns: a new #GNotification instance 181 * 182 * Since: 2.40 183 */ 184 GNotification * 185 g_notification_new (const gchar *title) 186 { 187 GNotification *notification; 188 189 g_return_val_if_fail (title != NULL, NULL); 190 191 notification = g_object_new (G_TYPE_NOTIFICATION, NULL); 192 notification->title = g_strdup (title); 193 194 return notification; 195 } 196 197 /*< private > 198 * g_notification_get_title: 199 * @notification: a #GNotification 200 * 201 * Gets the title of @notification. 202 * 203 * Returns: the title of @notification 204 * 205 * Since: 2.40 206 */ 207 const gchar * 208 g_notification_get_title (GNotification *notification) 209 { 210 g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL); 211 212 return notification->title; 213 } 214 215 /** 216 * g_notification_set_title: 217 * @notification: a #GNotification 218 * @title: the new title for @notification 219 * 220 * Sets the title of @notification to @title. 221 * 222 * Since: 2.40 223 */ 224 void 225 g_notification_set_title (GNotification *notification, 226 const gchar *title) 227 { 228 g_return_if_fail (G_IS_NOTIFICATION (notification)); 229 g_return_if_fail (title != NULL); 230 231 g_free (notification->title); 232 233 notification->title = g_strdup (title); 234 } 235 236 /*< private > 237 * g_notification_get_body: 238 * @notification: a #GNotification 239 * 240 * Gets the current body of @notification. 241 * 242 * Returns: (nullable): the body of @notification 243 * 244 * Since: 2.40 245 */ 246 const gchar * 247 g_notification_get_body (GNotification *notification) 248 { 249 g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL); 250 251 return notification->body; 252 } 253 254 /** 255 * g_notification_set_body: 256 * @notification: a #GNotification 257 * @body: (nullable): the new body for @notification, or %NULL 258 * 259 * Sets the body of @notification to @body. 260 * 261 * Since: 2.40 262 */ 263 void 264 g_notification_set_body (GNotification *notification, 265 const gchar *body) 266 { 267 g_return_if_fail (G_IS_NOTIFICATION (notification)); 268 g_return_if_fail (body != NULL); 269 270 g_free (notification->body); 271 272 notification->body = g_strdup (body); 273 } 274 275 /*< private > 276 * g_notification_get_icon: 277 * @notification: a #GNotification 278 * 279 * Gets the icon currently set on @notification. 280 * 281 * Returns: (transfer none): the icon associated with @notification 282 * 283 * Since: 2.40 284 */ 285 GIcon * 286 g_notification_get_icon (GNotification *notification) 287 { 288 g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL); 289 290 return notification->icon; 291 } 292 293 /** 294 * g_notification_set_icon: 295 * @notification: a #GNotification 296 * @icon: the icon to be shown in @notification, as a #GIcon 297 * 298 * Sets the icon of @notification to @icon. 299 * 300 * Since: 2.40 301 */ 302 void 303 g_notification_set_icon (GNotification *notification, 304 GIcon *icon) 305 { 306 g_return_if_fail (G_IS_NOTIFICATION (notification)); 307 308 if (notification->icon) 309 g_object_unref (notification->icon); 310 311 notification->icon = g_object_ref (icon); 312 } 313 314 /*< private > 315 * g_notification_get_priority: 316 * @notification: a #GNotification 317 * 318 * Returns the priority of @notification 319 * 320 * Since: 2.42 321 */ 322 GNotificationPriority 323 g_notification_get_priority (GNotification *notification) 324 { 325 g_return_val_if_fail (G_IS_NOTIFICATION (notification), G_NOTIFICATION_PRIORITY_NORMAL); 326 327 return notification->priority; 328 } 329 330 /** 331 * g_notification_set_urgent: 332 * @notification: a #GNotification 333 * @urgent: %TRUE if @notification is urgent 334 * 335 * Deprecated in favor of g_notification_set_priority(). 336 * 337 * Since: 2.40 338 * Deprecated: 2.42: Since 2.42, this has been deprecated in favour of 339 * g_notification_set_priority(). 340 */ 341 void 342 g_notification_set_urgent (GNotification *notification, 343 gboolean urgent) 344 { 345 g_return_if_fail (G_IS_NOTIFICATION (notification)); 346 347 notification->priority = urgent ? 348 G_NOTIFICATION_PRIORITY_URGENT : 349 G_NOTIFICATION_PRIORITY_NORMAL; 350 } 351 352 /*< private > 353 * g_notification_get_category: 354 * @notification: a #GNotification 355 * 356 * Gets the cateogry of @notification. 357 * 358 * This will be %NULL if no category is set. 359 * 360 * Returns: (nullable): the cateogry of @notification 361 * 362 * Since: 2.70 363 */ 364 const gchar * 365 g_notification_get_category (GNotification *notification) 366 { 367 g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL); 368 369 return notification->category; 370 } 371 372 /** 373 * g_notification_set_category: 374 * @notification: a #GNotification 375 * @category: (nullable): the category for @notification, or %NULL for no category 376 * 377 * Sets the type of @notification to @category. Categories have a main 378 * type like `email`, `im` or `device` and can have a detail separated 379 * by a `.`, e.g. `im.received` or `email.arrived`. Setting the category 380 * helps the notification server to select proper feedback to the user. 381 * 382 * Standard categories are [listed in the specification](https://specifications.freedesktop.org/notification-spec/latest/ar01s06.html). 383 * 384 * Since: 2.70 385 */ 386 void 387 g_notification_set_category (GNotification *notification, 388 const gchar *category) 389 { 390 g_return_if_fail (G_IS_NOTIFICATION (notification)); 391 g_return_if_fail (category == NULL || *category != '\0'); 392 393 g_free (notification->category); 394 395 notification->category = g_strdup (category); 396 } 397 398 /** 399 * g_notification_set_priority: 400 * @notification: a #GNotification 401 * @priority: a #GNotificationPriority 402 * 403 * Sets the priority of @notification to @priority. See 404 * #GNotificationPriority for possible values. 405 */ 406 void 407 g_notification_set_priority (GNotification *notification, 408 GNotificationPriority priority) 409 { 410 g_return_if_fail (G_IS_NOTIFICATION (notification)); 411 412 notification->priority = priority; 413 } 414 415 /** 416 * g_notification_add_button: 417 * @notification: a #GNotification 418 * @label: label of the button 419 * @detailed_action: a detailed action name 420 * 421 * Adds a button to @notification that activates the action in 422 * @detailed_action when clicked. That action must be an 423 * application-wide action (starting with "app."). If @detailed_action 424 * contains a target, the action will be activated with that target as 425 * its parameter. 426 * 427 * See g_action_parse_detailed_name() for a description of the format 428 * for @detailed_action. 429 * 430 * Since: 2.40 431 */ 432 void 433 g_notification_add_button (GNotification *notification, 434 const gchar *label, 435 const gchar *detailed_action) 436 { 437 gchar *action; 438 GVariant *target; 439 GError *error = NULL; 440 441 g_return_if_fail (detailed_action != NULL); 442 443 if (!g_action_parse_detailed_name (detailed_action, &action, &target, &error)) 444 { 445 g_warning ("%s: %s", G_STRFUNC, error->message); 446 g_error_free (error); 447 return; 448 } 449 450 g_notification_add_button_with_target_value (notification, label, action, target); 451 452 g_free (action); 453 if (target) 454 g_variant_unref (target); 455 } 456 457 /** 458 * g_notification_add_button_with_target: (skip) 459 * @notification: a #GNotification 460 * @label: label of the button 461 * @action: an action name 462 * @target_format: (nullable): a #GVariant format string, or %NULL 463 * @...: positional parameters, as determined by @target_format 464 * 465 * Adds a button to @notification that activates @action when clicked. 466 * @action must be an application-wide action (it must start with "app."). 467 * 468 * If @target_format is given, it is used to collect remaining 469 * positional parameters into a #GVariant instance, similar to 470 * g_variant_new(). @action will be activated with that #GVariant as its 471 * parameter. 472 * 473 * Since: 2.40 474 */ 475 void 476 g_notification_add_button_with_target (GNotification *notification, 477 const gchar *label, 478 const gchar *action, 479 const gchar *target_format, 480 ...) 481 { 482 va_list args; 483 GVariant *target = NULL; 484 485 if (target_format) 486 { 487 va_start (args, target_format); 488 target = g_variant_new_va (target_format, NULL, &args); 489 va_end (args); 490 } 491 492 g_notification_add_button_with_target_value (notification, label, action, target); 493 } 494 495 /** 496 * g_notification_add_button_with_target_value: (rename-to g_notification_add_button_with_target) 497 * @notification: a #GNotification 498 * @label: label of the button 499 * @action: an action name 500 * @target: (nullable): a #GVariant to use as @action's parameter, or %NULL 501 * 502 * Adds a button to @notification that activates @action when clicked. 503 * @action must be an application-wide action (it must start with "app."). 504 * 505 * If @target is non-%NULL, @action will be activated with @target as 506 * its parameter. 507 * 508 * Since: 2.40 509 */ 510 void 511 g_notification_add_button_with_target_value (GNotification *notification, 512 const gchar *label, 513 const gchar *action, 514 GVariant *target) 515 { 516 Button *button; 517 518 g_return_if_fail (G_IS_NOTIFICATION (notification)); 519 g_return_if_fail (label != NULL); 520 g_return_if_fail (action != NULL && g_action_name_is_valid (action)); 521 522 if (!g_str_has_prefix (action, "app.")) 523 { 524 g_warning ("%s: action '%s' does not start with 'app.'." 525 "This is unlikely to work properly.", G_STRFUNC, action); 526 } 527 528 button = g_slice_new0 (Button); 529 button->label = g_strdup (label); 530 button->action_name = g_strdup (action); 531 532 if (target) 533 button->target = g_variant_ref_sink (target); 534 535 g_ptr_array_add (notification->buttons, button); 536 } 537 538 /*< private > 539 * g_notification_get_n_buttons: 540 * @notification: a #GNotification 541 * 542 * Returns: the amount of buttons added to @notification. 543 */ 544 guint 545 g_notification_get_n_buttons (GNotification *notification) 546 { 547 return notification->buttons->len; 548 } 549 550 /*< private > 551 * g_notification_get_button: 552 * @notification: a #GNotification 553 * @index: index of the button 554 * @label: (): return location for the button's label 555 * @action: (): return location for the button's associated action 556 * @target: (): return location for the target @action should be 557 * activated with 558 * 559 * Returns a description of a button that was added to @notification 560 * with g_notification_add_button(). 561 * 562 * @index must be smaller than the value returned by 563 * g_notification_get_n_buttons(). 564 */ 565 void 566 g_notification_get_button (GNotification *notification, 567 gint index, 568 gchar **label, 569 gchar **action, 570 GVariant **target) 571 { 572 Button *button; 573 574 button = g_ptr_array_index (notification->buttons, index); 575 576 if (label) 577 *label = g_strdup (button->label); 578 579 if (action) 580 *action = g_strdup (button->action_name); 581 582 if (target) 583 *target = button->target ? g_variant_ref (button->target) : NULL; 584 } 585 586 /*< private > 587 * g_notification_get_button_with_action: 588 * @notification: a #GNotification 589 * @action: an action name 590 * 591 * Returns the index of the button in @notification that is associated 592 * with @action, or -1 if no such button exists. 593 */ 594 gint 595 g_notification_get_button_with_action (GNotification *notification, 596 const gchar *action) 597 { 598 guint i; 599 600 for (i = 0; i < notification->buttons->len; i++) 601 { 602 Button *button; 603 604 button = g_ptr_array_index (notification->buttons, i); 605 if (g_str_equal (action, button->action_name)) 606 return i; 607 } 608 609 return -1; 610 } 611 612 613 /*< private > 614 * g_notification_get_default_action: 615 * @notification: a #GNotification 616 * @action: (nullable): return location for the default action 617 * @target: (nullable): return location for the target of the default action 618 * 619 * Gets the action and target for the default action of @notification. 620 * 621 * Returns: %TRUE if @notification has a default action 622 */ 623 gboolean 624 g_notification_get_default_action (GNotification *notification, 625 gchar **action, 626 GVariant **target) 627 { 628 if (notification->default_action == NULL) 629 return FALSE; 630 631 if (action) 632 *action = g_strdup (notification->default_action); 633 634 if (target) 635 { 636 if (notification->default_action_target) 637 *target = g_variant_ref (notification->default_action_target); 638 else 639 *target = NULL; 640 } 641 642 return TRUE; 643 } 644 645 /** 646 * g_notification_set_default_action: 647 * @notification: a #GNotification 648 * @detailed_action: a detailed action name 649 * 650 * Sets the default action of @notification to @detailed_action. This 651 * action is activated when the notification is clicked on. 652 * 653 * The action in @detailed_action must be an application-wide action (it 654 * must start with "app."). If @detailed_action contains a target, the 655 * given action will be activated with that target as its parameter. 656 * See g_action_parse_detailed_name() for a description of the format 657 * for @detailed_action. 658 * 659 * When no default action is set, the application that the notification 660 * was sent on is activated. 661 * 662 * Since: 2.40 663 */ 664 void 665 g_notification_set_default_action (GNotification *notification, 666 const gchar *detailed_action) 667 { 668 gchar *action; 669 GVariant *target; 670 GError *error = NULL; 671 672 if (!g_action_parse_detailed_name (detailed_action, &action, &target, &error)) 673 { 674 g_warning ("%s: %s", G_STRFUNC, error->message); 675 g_error_free (error); 676 return; 677 } 678 679 g_notification_set_default_action_and_target_value (notification, action, target); 680 681 g_free (action); 682 if (target) 683 g_variant_unref (target); 684 } 685 686 /** 687 * g_notification_set_default_action_and_target: (skip) 688 * @notification: a #GNotification 689 * @action: an action name 690 * @target_format: (nullable): a #GVariant format string, or %NULL 691 * @...: positional parameters, as determined by @target_format 692 * 693 * Sets the default action of @notification to @action. This action is 694 * activated when the notification is clicked on. It must be an 695 * application-wide action (it must start with "app."). 696 * 697 * If @target_format is given, it is used to collect remaining 698 * positional parameters into a #GVariant instance, similar to 699 * g_variant_new(). @action will be activated with that #GVariant as its 700 * parameter. 701 * 702 * When no default action is set, the application that the notification 703 * was sent on is activated. 704 * 705 * Since: 2.40 706 */ 707 void 708 g_notification_set_default_action_and_target (GNotification *notification, 709 const gchar *action, 710 const gchar *target_format, 711 ...) 712 { 713 va_list args; 714 GVariant *target = NULL; 715 716 if (target_format) 717 { 718 va_start (args, target_format); 719 target = g_variant_new_va (target_format, NULL, &args); 720 va_end (args); 721 } 722 723 g_notification_set_default_action_and_target_value (notification, action, target); 724 } 725 726 /** 727 * g_notification_set_default_action_and_target_value: (rename-to g_notification_set_default_action_and_target) 728 * @notification: a #GNotification 729 * @action: an action name 730 * @target: (nullable): a #GVariant to use as @action's parameter, or %NULL 731 * 732 * Sets the default action of @notification to @action. This action is 733 * activated when the notification is clicked on. It must be an 734 * application-wide action (start with "app."). 735 * 736 * If @target is non-%NULL, @action will be activated with @target as 737 * its parameter. 738 * 739 * When no default action is set, the application that the notification 740 * was sent on is activated. 741 * 742 * Since: 2.40 743 */ 744 void 745 g_notification_set_default_action_and_target_value (GNotification *notification, 746 const gchar *action, 747 GVariant *target) 748 { 749 g_return_if_fail (G_IS_NOTIFICATION (notification)); 750 g_return_if_fail (action != NULL && g_action_name_is_valid (action)); 751 752 if (!g_str_has_prefix (action, "app.")) 753 { 754 g_warning ("%s: action '%s' does not start with 'app.'." 755 "This is unlikely to work properly.", G_STRFUNC, action); 756 } 757 758 g_free (notification->default_action); 759 g_clear_pointer (¬ification->default_action_target, g_variant_unref); 760 761 notification->default_action = g_strdup (action); 762 763 if (target) 764 notification->default_action_target = g_variant_ref_sink (target); 765 } 766 767 static GVariant * 768 g_notification_serialize_button (Button *button) 769 { 770 GVariantBuilder builder; 771 772 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); 773 774 g_variant_builder_add (&builder, "{sv}", "label", g_variant_new_string (button->label)); 775 g_variant_builder_add (&builder, "{sv}", "action", g_variant_new_string (button->action_name)); 776 777 if (button->target) 778 g_variant_builder_add (&builder, "{sv}", "target", button->target); 779 780 return g_variant_builder_end (&builder); 781 } 782 783 static GVariant * 784 g_notification_get_priority_nick (GNotification *notification) 785 { 786 GEnumClass *enum_class; 787 GEnumValue *value; 788 GVariant *nick; 789 790 enum_class = g_type_class_ref (G_TYPE_NOTIFICATION_PRIORITY); 791 value = g_enum_get_value (enum_class, g_notification_get_priority (notification)); 792 g_assert (value != NULL); 793 nick = g_variant_new_string (value->value_nick); 794 g_type_class_unref (enum_class); 795 796 return nick; 797 } 798 799 /*< private > 800 * g_notification_serialize: 801 * 802 * Serializes @notification into a floating variant of type a{sv}. 803 * 804 * Returns: the serialized @notification as a floating variant. 805 */ 806 GVariant * 807 g_notification_serialize (GNotification *notification) 808 { 809 GVariantBuilder builder; 810 811 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); 812 813 if (notification->title) 814 g_variant_builder_add (&builder, "{sv}", "title", g_variant_new_string (notification->title)); 815 816 if (notification->body) 817 g_variant_builder_add (&builder, "{sv}", "body", g_variant_new_string (notification->body)); 818 819 if (notification->icon) 820 { 821 GVariant *serialized_icon; 822 823 if ((serialized_icon = g_icon_serialize (notification->icon))) 824 { 825 g_variant_builder_add (&builder, "{sv}", "icon", serialized_icon); 826 g_variant_unref (serialized_icon); 827 } 828 } 829 830 g_variant_builder_add (&builder, "{sv}", "priority", g_notification_get_priority_nick (notification)); 831 832 if (notification->default_action) 833 { 834 g_variant_builder_add (&builder, "{sv}", "default-action", 835 g_variant_new_string (notification->default_action)); 836 837 if (notification->default_action_target) 838 g_variant_builder_add (&builder, "{sv}", "default-action-target", 839 notification->default_action_target); 840 } 841 842 if (notification->buttons->len > 0) 843 { 844 GVariantBuilder actions_builder; 845 guint i; 846 847 g_variant_builder_init (&actions_builder, G_VARIANT_TYPE ("aa{sv}")); 848 849 for (i = 0; i < notification->buttons->len; i++) 850 { 851 Button *button = g_ptr_array_index (notification->buttons, i); 852 g_variant_builder_add (&actions_builder, "@a{sv}", g_notification_serialize_button (button)); 853 } 854 855 g_variant_builder_add (&builder, "{sv}", "buttons", g_variant_builder_end (&actions_builder)); 856 } 857 858 return g_variant_builder_end (&builder); 859 } 860