LCOV - code coverage report
Current view: top level - src/libvalent/device - valent-device-plugin.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 92.3 % 117 108
Test Date: 2024-12-21 23:29:11 Functions: 100.0 % 15 15
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 50.6 % 158 80

             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-device-plugin"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <gio/gio.h>
       9                 :             : #include <json-glib/json-glib.h>
      10                 :             : #include <libpeas.h>
      11                 :             : #include <libvalent-core.h>
      12                 :             : 
      13                 :             : #include "valent-device.h"
      14                 :             : #include "valent-device-plugin.h"
      15                 :             : #include "valent-packet.h"
      16                 :             : 
      17                 :             : /**
      18                 :             :  * ValentDevicePlugin:
      19                 :             :  *
      20                 :             :  * An abstract base class for device plugins.
      21                 :             :  *
      22                 :             :  * `ValentDevicePlugin` is a base class for plugins that operate in the scope of
      23                 :             :  * a single device. This usually means communicating with other devices, however
      24                 :             :  * plugins aren't required to be packet based.
      25                 :             :  *
      26                 :             :  * ## Plugin Requirements
      27                 :             :  *
      28                 :             :  * Device plugins essentially have two sets of dependent conditions for being
      29                 :             :  * enabled. Plugins become available (i.e. can be enabled) when any of the
      30                 :             :  * following are true:
      31                 :             :  *
      32                 :             :  * - any of the device's outgoing capabilities match any of the plugin's
      33                 :             :  *   incoming capabilities
      34                 :             :  * - any of the device's incoming capabilities match any of the plugin's
      35                 :             :  *   outgoing capabilities
      36                 :             :  * - the plugin doesn't list any capabilities (eg. a non-packet based plugin)
      37                 :             :  *
      38                 :             :  * When a plugin becomes available it may be enabled, disabled and configured.
      39                 :             :  *
      40                 :             :  * ## Plugin Actions
      41                 :             :  *
      42                 :             :  * `ValentDevicePlugin` implements the [iface@Gio.ActionGroup] and
      43                 :             :  * [iface@Gio.ActionMap] interfaces, providing a simple way for plugins to
      44                 :             :  * expose functions and states. Each [iface@Gio.Action] added to the action map
      45                 :             :  * will be included in the device action group with the plugin's module name as
      46                 :             :  * a prefix (eg. `share.uri`).
      47                 :             :  *
      48                 :             :  * If the [class@Valent.DeviceManager] is exported on D-Bus, the actions will be
      49                 :             :  * exported along with the [class@Valent.Device].
      50                 :             :  *
      51                 :             :  * ## Implementation Notes
      52                 :             :  *
      53                 :             :  * Implementations that define `X-DevicePluginIncoming` in the `.plugin` file
      54                 :             :  * must override [vfunc@Valent.DevicePlugin.handle_packet] to handle incoming
      55                 :             :  * packets. Implementations that depend on the device state, especially those
      56                 :             :  * that define `X-DevicePluginOutgoing` in the `.plugin` file, should override
      57                 :             :  * [vfunc@Valent.DevicePlugin.update_state].
      58                 :             :  *
      59                 :             :  * ## `.plugin` File
      60                 :             :  *
      61                 :             :  * Implementations may define the following extra fields in the `.plugin` file:
      62                 :             :  *
      63                 :             :  * - `X-DevicePluginIncoming`
      64                 :             :  *
      65                 :             :  *     A list of packet types (eg. `kdeconnect.ping`) separated by semi-colons
      66                 :             :  *     indicating the packets that the plugin can handle.
      67                 :             :  *
      68                 :             :  * - `X-DevicePluginOutgoing`
      69                 :             :  *
      70                 :             :  *     A list of packet types (eg. `kdeconnect.share.request`) separated by
      71                 :             :  *     semi-colons indicating the packets that the plugin may send.
      72                 :             :  *
      73                 :             :  * - `X-DevicePluginSettings`
      74                 :             :  *
      75                 :             :  *     A [class@Gio.Settings] schema ID for the plugin's settings. See
      76                 :             :  *     [method@Valent.Context.get_plugin_settings] for more information.
      77                 :             :  *
      78                 :             :  * Since: 1.0
      79                 :             :  */
      80                 :             : 
      81   [ +  +  +  - ]:        4474 : G_DEFINE_ABSTRACT_TYPE (ValentDevicePlugin, valent_device_plugin, VALENT_TYPE_EXTENSION)
      82                 :             : 
      83                 :             : /* LCOV_EXCL_START */
      84                 :             : static void
      85                 :             : valent_device_plugin_real_handle_packet (ValentDevicePlugin *plugin,
      86                 :             :                                          const char         *type,
      87                 :             :                                          JsonNode           *packet)
      88                 :             : {
      89                 :             :   g_assert (VALENT_IS_DEVICE_PLUGIN (plugin));
      90                 :             :   g_assert (type != NULL && *type != '\0');
      91                 :             :   g_assert (VALENT_IS_PACKET (packet));
      92                 :             : 
      93                 :             :   g_critical ("%s: expected handler for \"%s\" packet",
      94                 :             :               G_OBJECT_TYPE_NAME (plugin),
      95                 :             :               type);
      96                 :             : }
      97                 :             : 
      98                 :             : static void
      99                 :             : valent_device_plugin_real_update_state (ValentDevicePlugin *plugin,
     100                 :             :                                         ValentDeviceState   state)
     101                 :             : {
     102                 :             :   g_assert (VALENT_IS_DEVICE_PLUGIN (plugin));
     103                 :             : }
     104                 :             : /* LCOV_EXCL_STOP */
     105                 :             : 
     106                 :             : static void
     107                 :          99 : valent_device_send_packet_cb (ValentDevice *device,
     108                 :             :                               GAsyncResult *result,
     109                 :             :                               gpointer      user_data)
     110                 :             : {
     111                 :         198 :   g_autoptr (GError) error = NULL;
     112                 :             : 
     113         [ +  + ]:          99 :   if (!valent_device_send_packet_finish (device, result, &error))
     114                 :             :     {
     115         [ -  + ]:           4 :       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
     116                 :           0 :         g_critical ("%s(): %s", G_STRFUNC, error->message);
     117         [ -  + ]:           4 :       else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_CONNECTED))
     118                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
     119         [ +  - ]:           4 :       else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     120                 :           4 :         g_debug ("%s(): %s", G_STRFUNC, error->message);
     121                 :             :     }
     122                 :          99 : }
     123                 :             : 
     124                 :             : /*
     125                 :             :  * GObject
     126                 :             :  */
     127                 :             : static void
     128                 :          58 : valent_device_plugin_class_init (ValentDevicePluginClass *klass)
     129                 :             : {
     130                 :          58 :   klass->handle_packet = valent_device_plugin_real_handle_packet;
     131                 :          58 :   klass->update_state = valent_device_plugin_real_update_state;
     132                 :             : }
     133                 :             : 
     134                 :             : static void
     135                 :         212 : valent_device_plugin_init (ValentDevicePlugin *self)
     136                 :             : {
     137                 :         212 : }
     138                 :             : 
     139                 :             : /**
     140                 :             :  * valent_device_plugin_queue_packet:
     141                 :             :  * @plugin: a `ValentDevicePlugin`
     142                 :             :  * @packet: a KDE Connect packet
     143                 :             :  *
     144                 :             :  * Queue a KDE Connect packet to be sent to the device this plugin is bound to.
     145                 :             :  *
     146                 :             :  * For notification of success, you may call [method@Valent.Resource.get_source]
     147                 :             :  * and then [method@Valent.Device.send_packet], but note that there can be no
     148                 :             :  * guarantee the remote device has received the packet.
     149                 :             :  *
     150                 :             :  * Since: 1.0
     151                 :             :  */
     152                 :             : void
     153                 :          99 : valent_device_plugin_queue_packet (ValentDevicePlugin *plugin,
     154                 :             :                                    JsonNode           *packet)
     155                 :             : {
     156                 :          99 :   ValentDevice *device = NULL;
     157                 :         198 :   g_autoptr (GCancellable) destroy = NULL;
     158                 :             : 
     159         [ +  - ]:          99 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     160         [ -  + ]:          99 :   g_return_if_fail (VALENT_IS_PACKET (packet));
     161                 :             : 
     162         [ +  - ]:          99 :   if ((device = valent_resource_get_source (VALENT_RESOURCE (plugin))) == NULL)
     163                 :             :     return;
     164                 :             : 
     165                 :          99 :   destroy = valent_object_ref_cancellable (VALENT_OBJECT (plugin));
     166         [ +  - ]:          99 :   valent_device_send_packet (device,
     167                 :             :                              packet,
     168                 :             :                              destroy,
     169                 :             :                              (GAsyncReadyCallback)valent_device_send_packet_cb,
     170                 :             :                              NULL);
     171                 :             : }
     172                 :             : 
     173                 :             : /**
     174                 :             :  * valent_device_plugin_show_notification:
     175                 :             :  * @plugin: a `ValentDevicePlugin`
     176                 :             :  * @id: an id for the notification
     177                 :             :  * @notification: a `GNotification`
     178                 :             :  *
     179                 :             :  * A convenience for showing a local notification.
     180                 :             :  *
     181                 :             :  * @id will be automatically prepended with the device ID and plugin module to
     182                 :             :  * prevent conflicting with other devices and plugins.
     183                 :             :  *
     184                 :             :  * Call [method@Valent.DevicePlugin.hide_notification] to make the same
     185                 :             :  * transformation on @id and withdraw the notification.
     186                 :             :  *
     187                 :             :  * Since: 1.0
     188                 :             :  */
     189                 :             : void
     190                 :          40 : valent_device_plugin_show_notification (ValentDevicePlugin *plugin,
     191                 :             :                                         const char         *id,
     192                 :             :                                         GNotification      *notification)
     193                 :             : {
     194                 :          40 :   GApplication *application = g_application_get_default ();
     195                 :          40 :   g_autoptr (ValentDevice) device = NULL;
     196   [ -  -  -  + ]:          40 :   g_autoptr (PeasPluginInfo) plugin_info = NULL;
     197   [ -  -  -  + ]:          40 :   g_autofree char *notification_id = NULL;
     198                 :             : 
     199         [ +  - ]:          40 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     200         [ -  + ]:          40 :   g_return_if_fail (id != NULL);
     201   [ +  -  +  -  :          40 :   g_return_if_fail (G_IS_NOTIFICATION (notification));
             -  +  -  - ]
     202                 :             : 
     203         [ -  + ]:          40 :   if G_UNLIKELY (application == NULL)
     204                 :             :     return;
     205                 :             : 
     206                 :           0 :   g_object_get (plugin,
     207                 :             :                 "plugin-info", &plugin_info,
     208                 :             :                 "source",      &device,
     209                 :             :                 NULL);
     210                 :           0 :   notification_id = g_strdup_printf ("%s::%s::%s",
     211                 :             :                                      valent_device_get_id (device),
     212                 :             :                                      peas_plugin_info_get_module_name (plugin_info),
     213                 :             :                                      id);
     214                 :           0 :   g_application_send_notification (application, notification_id, notification);
     215                 :             : }
     216                 :             : 
     217                 :             : /**
     218                 :             :  * valent_device_plugin_hide_notification:
     219                 :             :  * @plugin: a `ValentDevicePlugin`
     220                 :             :  * @id: an id for the notification
     221                 :             :  *
     222                 :             :  * A convenience for withdrawing a notification.
     223                 :             :  *
     224                 :             :  * This method will withdraw a notification shown with
     225                 :             :  * [method@Valent.DevicePlugin.show_notification].
     226                 :             :  *
     227                 :             :  * Since: 1.0
     228                 :             :  */
     229                 :             : void
     230                 :          17 : valent_device_plugin_hide_notification (ValentDevicePlugin *plugin,
     231                 :             :                                         const char         *id)
     232                 :             : {
     233                 :          17 :   GApplication *application = g_application_get_default ();
     234                 :          17 :   g_autoptr (ValentDevice) device = NULL;
     235   [ -  -  -  + ]:          17 :   g_autoptr (PeasPluginInfo) plugin_info = NULL;
     236   [ -  -  -  + ]:          17 :   g_autofree char *notification_id = NULL;
     237                 :             : 
     238         [ +  - ]:          17 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     239         [ -  + ]:          17 :   g_return_if_fail (id != NULL);
     240                 :             : 
     241         [ -  + ]:          17 :   if G_UNLIKELY (application == NULL)
     242                 :             :     return;
     243                 :             : 
     244                 :           0 :   g_object_get (plugin,
     245                 :             :                 "plugin-info", &plugin_info,
     246                 :             :                 "source",      &device,
     247                 :             :                 NULL);
     248                 :           0 :   notification_id = g_strdup_printf ("%s::%s::%s",
     249                 :             :                                      valent_device_get_id (device),
     250                 :             :                                      peas_plugin_info_get_module_name (plugin_info),
     251                 :             :                                      id);
     252                 :           0 :   g_application_withdraw_notification (application, notification_id);
     253                 :             : }
     254                 :             : 
     255                 :             : /**
     256                 :             :  * valent_device_plugin_handle_packet: (virtual handle_packet)
     257                 :             :  * @plugin: a `ValentDevicePlugin`
     258                 :             :  * @type: a KDE Connect packet type
     259                 :             :  * @packet: a KDE Connect packet
     260                 :             :  *
     261                 :             :  * Handle a packet from the device the plugin is bound to.
     262                 :             :  *
     263                 :             :  * This is called when the device receives a packet type included in the
     264                 :             :  * `X-DevicePluginIncoming` field of the `.plugin` file.
     265                 :             :  *
     266                 :             :  * This is optional for implementations which do not register any incoming
     267                 :             :  * capabilities, such as plugins that do not provide packet-based functionality.
     268                 :             :  *
     269                 :             :  * Since: 1.0
     270                 :             :  */
     271                 :             : void
     272                 :         118 : valent_device_plugin_handle_packet (ValentDevicePlugin *plugin,
     273                 :             :                                     const char         *type,
     274                 :             :                                     JsonNode           *packet)
     275                 :             : {
     276                 :         118 :   VALENT_ENTRY;
     277                 :             : 
     278         [ +  - ]:         118 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     279   [ +  -  -  + ]:         118 :   g_return_if_fail (type != NULL && *type != '\0');
     280         [ -  + ]:         118 :   g_return_if_fail (VALENT_IS_PACKET (packet));
     281                 :             : 
     282                 :         118 :   VALENT_DEVICE_PLUGIN_GET_CLASS (plugin)->handle_packet (plugin, type, packet);
     283                 :             : 
     284                 :         118 :   VALENT_EXIT;
     285                 :             : }
     286                 :             : 
     287                 :             : /**
     288                 :             :  * valent_device_plugin_update_state: (virtual update_state)
     289                 :             :  * @plugin: a `ValentDevicePlugin`
     290                 :             :  * @state: a `ValentDeviceState`
     291                 :             :  *
     292                 :             :  * Update the plugin based on the new state of the device.
     293                 :             :  *
     294                 :             :  * This function is called when the connected or paired state of the device
     295                 :             :  * changes. This may be used to configure actions, event handlers that may
     296                 :             :  * trigger outgoing packets and exchange connect-time data with the device.
     297                 :             :  *
     298                 :             :  * This is optional for all implementations as plugins aren't required to be
     299                 :             :  * dependent on the device state.
     300                 :             :  *
     301                 :             :  * Since: 1.0
     302                 :             :  */
     303                 :             : void
     304                 :         592 : valent_device_plugin_update_state (ValentDevicePlugin *plugin,
     305                 :             :                                    ValentDeviceState   state)
     306                 :             : {
     307                 :         592 :   VALENT_ENTRY;
     308                 :             : 
     309         [ +  - ]:         592 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     310                 :             : 
     311                 :         592 :   VALENT_DEVICE_PLUGIN_GET_CLASS (plugin)->update_state (plugin, state);
     312                 :             : 
     313                 :         592 :   VALENT_EXIT;
     314                 :             : }
     315                 :             : 
     316                 :             : static int
     317                 :         159 : _g_menu_find_action (GMenuModel *menu,
     318                 :             :                      const char *action)
     319                 :             : {
     320                 :         159 :   int i, n_items;
     321                 :             : 
     322   [ +  -  +  -  :         159 :   g_assert (G_IS_MENU_MODEL (menu));
             +  -  -  + ]
     323         [ -  + ]:         159 :   g_assert (action != NULL);
     324                 :             : 
     325                 :         159 :   n_items = g_menu_model_get_n_items (menu);
     326                 :             : 
     327         [ +  + ]:         365 :   for (i = 0; i < n_items; i++)
     328                 :             :     {
     329                 :          55 :       g_autofree char *item_str = NULL;
     330                 :             : 
     331                 :          55 :       g_menu_model_get_item_attribute (menu, i, "action", "s", &item_str);
     332                 :             : 
     333         [ +  + ]:          55 :       if (g_strcmp0 (item_str, action) == 0)
     334                 :           8 :         return i;
     335                 :             :     }
     336                 :             : 
     337                 :             :   return -1;
     338                 :             : }
     339                 :             : 
     340                 :             : /**
     341                 :             :  * valent_device_plugin_set_menu_action:
     342                 :             :  * @plugin: a `ValentDevicePlugin`
     343                 :             :  * @action: a `GAction` name
     344                 :             :  * @label: (nullable): a label for the action
     345                 :             :  * @icon_name: (nullable): an icon for the action
     346                 :             :  *
     347                 :             :  * Set or remove a device menu action by [iface@Gio.Action] name.
     348                 :             :  *
     349                 :             :  * If @label and @icon are %NULL, @action will be removed from the menu.
     350                 :             :  *
     351                 :             :  * Since: 1.0
     352                 :             :  */
     353                 :             : void
     354                 :         142 : valent_device_plugin_set_menu_action (ValentDevicePlugin *plugin,
     355                 :             :                                       const char         *action,
     356                 :             :                                       const char         *label,
     357                 :             :                                       const char         *icon_name)
     358                 :             : {
     359                 :         284 :   g_autoptr (GMenuItem) item = NULL;
     360                 :             : 
     361         [ +  - ]:         142 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     362   [ +  -  -  + ]:         142 :   g_return_if_fail (action != NULL && *action != '\0');
     363   [ +  -  +  -  :         142 :   g_return_if_fail ((label == NULL && icon_name == NULL) ||
                   -  + ]
     364                 :             :                     (label != NULL && *label != '\0'));
     365                 :             : 
     366         [ +  - ]:         142 :   if (label != NULL)
     367                 :             :     {
     368                 :         284 :       g_autoptr (GIcon) icon = NULL;
     369                 :             : 
     370         [ +  - ]:         142 :       if (icon_name != NULL)
     371                 :         142 :         icon = g_themed_icon_new (icon_name);
     372                 :             : 
     373                 :         142 :       item = g_menu_item_new (label, action);
     374                 :         142 :       g_menu_item_set_icon (item, icon);
     375         [ +  - ]:         142 :       g_menu_item_set_attribute (item, "hidden-when", "s", "action-disabled");
     376                 :             :     }
     377                 :             : 
     378         [ +  - ]:         142 :   valent_device_plugin_set_menu_item (plugin, action, item);
     379                 :             : }
     380                 :             : 
     381                 :             : /**
     382                 :             :  * valent_device_plugin_set_menu_item:
     383                 :             :  * @plugin: a `ValentDevicePlugin`
     384                 :             :  * @action: a `GAction` name
     385                 :             :  * @item: (nullable): a `GMenuItem`
     386                 :             :  *
     387                 :             :  * Set or remove a device [class@Gio.MenuItem] by [iface@Gio.Action] name.
     388                 :             :  *
     389                 :             :  * If @item is %NULL, @action will be removed from the menu.
     390                 :             :  *
     391                 :             :  * Since: 1.0
     392                 :             :  */
     393                 :             : void
     394                 :         415 : valent_device_plugin_set_menu_item (ValentDevicePlugin *plugin,
     395                 :             :                                     const char         *action,
     396                 :             :                                     GMenuItem          *item)
     397                 :             : {
     398                 :         415 :   ValentDevice *device = NULL;
     399                 :         415 :   GMenuModel *menu;
     400                 :         415 :   int index_ = -1;
     401                 :             : 
     402         [ +  - ]:         415 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     403   [ +  -  -  + ]:         415 :   g_return_if_fail (action != NULL && *action != '\0');
     404   [ +  +  +  -  :         415 :   g_return_if_fail (item == NULL || G_IS_MENU_ITEM (item));
             -  +  -  - ]
     405                 :             : 
     406                 :             :   /* NOTE: this method may be called by plugins in their `dispose()` */
     407         [ +  + ]:         415 :   if ((device = valent_resource_get_source (VALENT_RESOURCE (plugin))) == NULL)
     408                 :             :     return;
     409                 :             : 
     410                 :         159 :   menu = valent_device_get_menu (device);
     411                 :         159 :   index_ = _g_menu_find_action (menu, action);
     412                 :             : 
     413         [ +  + ]:         159 :   if (index_ > -1)
     414                 :           8 :     g_menu_remove (G_MENU (menu), index_);
     415                 :             : 
     416         [ +  + ]:         159 :   if (item != NULL)
     417                 :             :     {
     418         [ -  + ]:         143 :       if (index_ > -1)
     419                 :           0 :         g_menu_insert_item (G_MENU (menu), index_, item);
     420                 :             :       else
     421                 :         143 :         g_menu_append_item (G_MENU (menu), item);
     422                 :             :     }
     423                 :             : }
     424                 :             : 
     425                 :             : /**
     426                 :             :  * valent_notification_set_device_action:
     427                 :             :  * @notification: a `GNotification`
     428                 :             :  * @device: a `ValentDevice`
     429                 :             :  * @action: the device action name
     430                 :             :  * @target: (nullable): the action target
     431                 :             :  *
     432                 :             :  * Set the default action for @notification. @action is wrapped in the special
     433                 :             :  * `device` action for @device, which allows it to be activated from the `app`
     434                 :             :  * action scope.
     435                 :             :  *
     436                 :             :  * Since: 1.0
     437                 :             :  */
     438                 :             : void
     439                 :           1 : valent_notification_set_device_action (GNotification *notification,
     440                 :             :                                        ValentDevice  *device,
     441                 :             :                                        const char    *action,
     442                 :             :                                        GVariant      *target)
     443                 :             : {
     444                 :           1 :   GVariantBuilder builder;
     445                 :             : 
     446   [ +  -  +  -  :           1 :   g_return_if_fail (G_IS_NOTIFICATION (notification));
             -  +  -  - ]
     447         [ -  + ]:           1 :   g_return_if_fail (VALENT_IS_DEVICE (device));
     448   [ +  -  -  + ]:           1 :   g_return_if_fail (action != NULL && *action != '\0');
     449                 :             : 
     450                 :           1 :   g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
     451                 :             : 
     452         [ +  - ]:           1 :   if (target != NULL)
     453                 :           1 :     g_variant_builder_add (&builder, "v", target);
     454                 :             : 
     455                 :           1 :   g_notification_set_default_action_and_target (notification,
     456                 :             :                                                 "app.device",
     457                 :             :                                                 "(ssav)",
     458                 :             :                                                 valent_device_get_id (device),
     459                 :             :                                                 action,
     460                 :             :                                                 &builder);
     461                 :             : }
     462                 :             : 
     463                 :             : /**
     464                 :             :  * valent_notification_add_device_button:
     465                 :             :  * @notification: a `GNotification`
     466                 :             :  * @device: a `ValentDevice`
     467                 :             :  * @label: the button label
     468                 :             :  * @action: the device action name
     469                 :             :  * @target: (nullable): the action target
     470                 :             :  *
     471                 :             :  * Add an action button to @notification. @action is wrapped in the special
     472                 :             :  * `device` action for @device, which allows it to be activated from the `app`
     473                 :             :  * action scope.
     474                 :             :  *
     475                 :             :  * Since: 1.0
     476                 :             :  */
     477                 :             : void
     478                 :          31 : valent_notification_add_device_button (GNotification *notification,
     479                 :             :                                        ValentDevice  *device,
     480                 :             :                                        const char    *label,
     481                 :             :                                        const char    *action,
     482                 :             :                                        GVariant      *target)
     483                 :             : {
     484                 :          31 :   GVariantBuilder builder;
     485                 :             : 
     486   [ +  -  +  -  :          31 :   g_return_if_fail (G_IS_NOTIFICATION (notification));
             -  +  -  - ]
     487         [ -  + ]:          31 :   g_return_if_fail (VALENT_IS_DEVICE (device));
     488   [ +  -  -  + ]:          31 :   g_return_if_fail (label != NULL && *label != '\0');
     489   [ +  -  -  + ]:          31 :   g_return_if_fail (action != NULL && *action != '\0');
     490                 :             : 
     491                 :          31 :   g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
     492                 :             : 
     493         [ +  + ]:          31 :   if (target != NULL)
     494                 :          26 :     g_variant_builder_add (&builder, "v", target);
     495                 :             : 
     496                 :          31 :   g_notification_add_button_with_target (notification,
     497                 :             :                                          label,
     498                 :             :                                          "app.device",
     499                 :             :                                          "(ssav)",
     500                 :             :                                          valent_device_get_id (device),
     501                 :             :                                          action,
     502                 :             :                                          &builder);
     503                 :             : }
     504                 :             : 
        

Generated by: LCOV version 2.0-1