LCOV - code coverage report
Current view: top level - glib/gio - gactiongroupexporter.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 203 218 93.1 %
Date: 2024-04-23 05:16:05 Functions: 13 13 100.0 %
Branches: 75 98 76.5 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright © 2010 Codethink Limited
       3                 :            :  * Copyright © 2011 Canonical Limited
       4                 :            :  *
       5                 :            :  * SPDX-License-Identifier: LGPL-2.1-or-later
       6                 :            :  *
       7                 :            :  * This library is free software; you can redistribute it and/or
       8                 :            :  * modify it under the terms of the GNU Lesser General Public
       9                 :            :  * License as published by the Free Software Foundation; either
      10                 :            :  * version 2.1 of the License, or (at your option) any later version.
      11                 :            :  *
      12                 :            :  * This library is distributed in the hope that it will be useful,
      13                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15                 :            :  * Lesser General Public License for more details.
      16                 :            :  *
      17                 :            :  * You should have received a copy of the GNU Lesser General
      18                 :            :  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19                 :            :  *
      20                 :            :  * Authors: Ryan Lortie <desrt@desrt.ca>
      21                 :            :  */
      22                 :            : 
      23                 :            : #include "config.h"
      24                 :            : 
      25                 :            : #include "gactiongroupexporter.h"
      26                 :            : 
      27                 :            : #include "gdbusmethodinvocation.h"
      28                 :            : #include "gremoteactiongroup.h"
      29                 :            : #include "gdbusintrospection.h"
      30                 :            : #include "gdbusconnection.h"
      31                 :            : #include "gactiongroup.h"
      32                 :            : #include "gdbuserror.h"
      33                 :            : 
      34                 :            : /**
      35                 :            :  * GActionGroupExporter:
      36                 :            :  *
      37                 :            :  * These functions support exporting a [class@Gio.ActionGroup] on D-Bus.
      38                 :            :  * The D-Bus interface that is used is a private implementation
      39                 :            :  * detail.
      40                 :            :  *
      41                 :            :  * To access an exported `GActionGroup` remotely, use
      42                 :            :  * [method@Gio.DBusActionGroup.get] to obtain a [class@Gio.DBusActionGroup].
      43                 :            :  */
      44                 :            : 
      45                 :            : static GVariant *
      46                 :         15 : g_action_group_describe_action (GActionGroup *action_group,
      47                 :            :                                 const gchar  *name)
      48                 :            : {
      49                 :            :   const GVariantType *type;
      50                 :            :   GVariantBuilder builder;
      51                 :            :   gboolean enabled;
      52                 :            :   GVariant *state;
      53                 :            : 
      54                 :         15 :   g_variant_builder_init (&builder, G_VARIANT_TYPE ("(bgav)"));
      55                 :            : 
      56                 :         15 :   enabled = g_action_group_get_action_enabled (action_group, name);
      57                 :         15 :   g_variant_builder_add (&builder, "b", enabled);
      58                 :            : 
      59         [ +  + ]:         15 :   if ((type = g_action_group_get_action_parameter_type (action_group, name)))
      60                 :            :     {
      61                 :          2 :       gchar *str = g_variant_type_dup_string (type);
      62                 :          2 :       g_variant_builder_add (&builder, "g", str);
      63                 :          2 :       g_free (str);
      64                 :            :     }
      65                 :            :   else
      66                 :         13 :     g_variant_builder_add (&builder, "g", "");
      67                 :            : 
      68                 :         15 :   g_variant_builder_open (&builder, G_VARIANT_TYPE ("av"));
      69         [ +  + ]:         15 :   if ((state = g_action_group_get_action_state (action_group, name)))
      70                 :            :     {
      71                 :          4 :       g_variant_builder_add (&builder, "v", state);
      72                 :          4 :       g_variant_unref (state);
      73                 :            :     }
      74                 :         15 :   g_variant_builder_close (&builder);
      75                 :            : 
      76                 :         15 :   return g_variant_builder_end (&builder);
      77                 :            : }
      78                 :            : 
      79                 :            : /* Using XML saves us dozens of relocations vs. using the introspection
      80                 :            :  * structure types.  We only need to burn cycles and memory if we
      81                 :            :  * actually use the exporter -- not in every single app using GIO.
      82                 :            :  *
      83                 :            :  * It's also a lot easier to read. :)
      84                 :            :  *
      85                 :            :  * For documentation of this interface, see
      86                 :            :  * https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
      87                 :            :  */
      88                 :            : const char org_gtk_Actions_xml[] =
      89                 :            :   "<node>"
      90                 :            :   "  <interface name='org.gtk.Actions'>"
      91                 :            :   "    <method name='List'>"
      92                 :            :   "      <arg type='as' name='list' direction='out'/>"
      93                 :            :   "    </method>"
      94                 :            :   "    <method name='Describe'>"
      95                 :            :   "      <arg type='s' name='action_name' direction='in'/>"
      96                 :            :   "      <arg type='(bgav)' name='description' direction='out'/>"
      97                 :            :   "    </method>"
      98                 :            :   "    <method name='DescribeAll'>"
      99                 :            :   "      <arg type='a{s(bgav)}' name='descriptions' direction='out'/>"
     100                 :            :   "    </method>"
     101                 :            :   "    <method name='Activate'>"
     102                 :            :   "      <arg type='s' name='action_name' direction='in'/>"
     103                 :            :   "      <arg type='av' name='parameter' direction='in'/>"
     104                 :            :   "      <arg type='a{sv}' name='platform_data' direction='in'/>"
     105                 :            :   "    </method>"
     106                 :            :   "    <method name='SetState'>"
     107                 :            :   "      <arg type='s' name='action_name' direction='in'/>"
     108                 :            :   "      <arg type='v' name='value' direction='in'/>"
     109                 :            :   "      <arg type='a{sv}' name='platform_data' direction='in'/>"
     110                 :            :   "    </method>"
     111                 :            :   "    <signal name='Changed'>"
     112                 :            :   "      <arg type='as' name='removals'/>"
     113                 :            :   "      <arg type='a{sb}' name='enable_changes'/>"
     114                 :            :   "      <arg type='a{sv}' name='state_changes'/>"
     115                 :            :   "      <arg type='a{s(bgav)}' name='additions'/>"
     116                 :            :   "    </signal>"
     117                 :            :   "  </interface>"
     118                 :            :   "</node>";
     119                 :            : 
     120                 :            : static GDBusInterfaceInfo *org_gtk_Actions;
     121                 :            : 
     122                 :            : typedef struct
     123                 :            : {
     124                 :            :   GActionGroup    *action_group;
     125                 :            :   GDBusConnection *connection;
     126                 :            :   GMainContext    *context;
     127                 :            :   gchar           *object_path;
     128                 :            :   GHashTable      *pending_changes;
     129                 :            :   GSource         *pending_source;
     130                 :            : } GActionGroupExporter;
     131                 :            : 
     132                 :            : #define ACTION_ADDED_EVENT             (1u<<0)
     133                 :            : #define ACTION_REMOVED_EVENT           (1u<<1)
     134                 :            : #define ACTION_STATE_CHANGED_EVENT     (1u<<2)
     135                 :            : #define ACTION_ENABLED_CHANGED_EVENT   (1u<<3)
     136                 :            : 
     137                 :            : static gboolean
     138                 :         10 : g_action_group_exporter_dispatch_events (gpointer user_data)
     139                 :            : {
     140                 :         10 :   GActionGroupExporter *exporter = user_data;
     141                 :            :   GVariantBuilder removes;
     142                 :            :   GVariantBuilder enabled_changes;
     143                 :            :   GVariantBuilder state_changes;
     144                 :            :   GVariantBuilder adds;
     145                 :            :   GHashTableIter iter;
     146                 :            :   gpointer value;
     147                 :            :   gpointer key;
     148                 :            : 
     149                 :         10 :   g_variant_builder_init (&removes, G_VARIANT_TYPE_STRING_ARRAY);
     150                 :         10 :   g_variant_builder_init (&enabled_changes, G_VARIANT_TYPE ("a{sb}"));
     151                 :         10 :   g_variant_builder_init (&state_changes, G_VARIANT_TYPE ("a{sv}"));
     152                 :         10 :   g_variant_builder_init (&adds, G_VARIANT_TYPE ("a{s(bgav)}"));
     153                 :            : 
     154                 :         10 :   g_hash_table_iter_init (&iter, exporter->pending_changes);
     155         [ +  + ]:         34 :   while (g_hash_table_iter_next (&iter, &key, &value))
     156                 :            :     {
     157                 :         14 :       guint events = GPOINTER_TO_INT (value);
     158                 :         14 :       const gchar *name = key;
     159                 :            : 
     160                 :            :       /* Adds and removes are incompatible with enabled or state
     161                 :            :        * changes, but we must report at least one event.
     162                 :            :        */
     163                 :         14 :       g_assert (((events & (ACTION_ENABLED_CHANGED_EVENT | ACTION_STATE_CHANGED_EVENT)) == 0) !=
     164                 :            :                 ((events & (ACTION_REMOVED_EVENT | ACTION_ADDED_EVENT)) == 0));
     165                 :            : 
     166         [ +  + ]:         14 :       if (events & ACTION_REMOVED_EVENT)
     167                 :          1 :         g_variant_builder_add (&removes, "s", name);
     168                 :            : 
     169         [ +  + ]:         14 :       if (events & ACTION_ENABLED_CHANGED_EVENT)
     170                 :            :         {
     171                 :            :           gboolean enabled;
     172                 :            : 
     173                 :          1 :           enabled = g_action_group_get_action_enabled (exporter->action_group, name);
     174                 :          1 :           g_variant_builder_add (&enabled_changes, "{sb}", name, enabled);
     175                 :            :         }
     176                 :            : 
     177         [ +  + ]:         14 :       if (events & ACTION_STATE_CHANGED_EVENT)
     178                 :            :         {
     179                 :            :           GVariant *state;
     180                 :            : 
     181                 :          5 :           state = g_action_group_get_action_state (exporter->action_group, name);
     182                 :          5 :           g_variant_builder_add (&state_changes, "{sv}", name, state);
     183                 :          5 :           g_variant_unref (state);
     184                 :            :         }
     185                 :            : 
     186         [ +  + ]:         14 :       if (events & ACTION_ADDED_EVENT)
     187                 :            :         {
     188                 :            :           GVariant *description;
     189                 :            : 
     190                 :          7 :           description = g_action_group_describe_action (exporter->action_group, name);
     191                 :          7 :           g_variant_builder_add (&adds, "{s@(bgav)}", name, description);
     192                 :            :         }
     193                 :            :     }
     194                 :            : 
     195                 :         10 :   g_hash_table_remove_all (exporter->pending_changes);
     196                 :            : 
     197                 :         10 :   g_dbus_connection_emit_signal (exporter->connection, NULL, exporter->object_path,
     198                 :            :                                  "org.gtk.Actions", "Changed",
     199                 :            :                                  g_variant_new ("(asa{sb}a{sv}a{s(bgav)})",
     200                 :            :                                                 &removes, &enabled_changes,
     201                 :            :                                                 &state_changes, &adds),
     202                 :            :                                  NULL);
     203                 :            : 
     204                 :         10 :   exporter->pending_source = NULL;
     205                 :            : 
     206                 :         10 :   return FALSE;
     207                 :            : }
     208                 :            : 
     209                 :            : static void
     210                 :         17 : g_action_group_exporter_flush_queue (GActionGroupExporter *exporter)
     211                 :            : {
     212         [ -  + ]:         17 :   if (exporter->pending_source)
     213                 :            :     {
     214                 :          0 :       g_source_destroy (exporter->pending_source);
     215                 :          0 :       g_action_group_exporter_dispatch_events (exporter);
     216                 :          0 :       g_assert (exporter->pending_source == NULL);
     217                 :            :     }
     218                 :         17 : }
     219                 :            : 
     220                 :            : static guint
     221                 :      99830 : g_action_group_exporter_get_events (GActionGroupExporter *exporter,
     222                 :            :                                     const gchar          *name)
     223                 :            : {
     224                 :      99830 :   return (gsize) g_hash_table_lookup (exporter->pending_changes, name);
     225                 :            : }
     226                 :            : 
     227                 :            : static void
     228                 :      99986 : g_action_group_exporter_set_events (GActionGroupExporter *exporter,
     229                 :            :                                     const gchar          *name,
     230                 :            :                                     guint                 events)
     231                 :            : {
     232                 :            :   gboolean have_events;
     233                 :            :   gboolean is_queued;
     234                 :            : 
     235         [ +  - ]:      99986 :   if (events != 0)
     236                 :     199985 :     g_hash_table_insert (exporter->pending_changes, g_strdup (name), GINT_TO_POINTER (events));
     237                 :            :   else
     238                 :          0 :     g_hash_table_remove (exporter->pending_changes, name);
     239                 :            : 
     240                 :      99984 :   have_events = g_hash_table_size (exporter->pending_changes) > 0;
     241                 :      99978 :   is_queued = exporter->pending_source != NULL;
     242                 :            : 
     243   [ +  #  +  + ]:      99978 :   if (have_events && !is_queued)
     244                 :            :     {
     245                 :            :       GSource *source;
     246                 :            : 
     247                 :      99977 :       source = g_idle_source_new ();
     248                 :      99993 :       exporter->pending_source = source;
     249                 :      99993 :       g_source_set_callback (source, g_action_group_exporter_dispatch_events, exporter, NULL);
     250                 :      99973 :       g_source_set_static_name (source, "[gio] g_action_group_exporter_dispatch_events");
     251                 :     100001 :       g_source_attach (source, exporter->context);
     252                 :      99985 :       g_source_unref (source);
     253                 :            :     }
     254                 :            : 
     255   [ -  +  -  - ]:      99980 :   if (!have_events && is_queued)
     256                 :            :     {
     257                 :          0 :       g_source_destroy (exporter->pending_source);
     258                 :          0 :       exporter->pending_source = NULL;
     259                 :            :     }
     260                 :      99980 : }
     261                 :            : 
     262                 :            : static void
     263                 :          7 : g_action_group_exporter_action_added (GActionGroup *action_group,
     264                 :            :                                       const gchar  *action_name,
     265                 :            :                                       gpointer      user_data)
     266                 :            : {
     267                 :          7 :   GActionGroupExporter *exporter = user_data;
     268                 :            :   guint event_mask;
     269                 :            : 
     270                 :          7 :   event_mask = g_action_group_exporter_get_events (exporter, action_name);
     271                 :            : 
     272                 :            :   /* The action is new, so we should not have any pending
     273                 :            :    * enabled-changed or state-changed signals for it.
     274                 :            :    */
     275                 :          7 :   g_assert (~event_mask & (ACTION_STATE_CHANGED_EVENT |
     276                 :            :                            ACTION_ENABLED_CHANGED_EVENT));
     277                 :            : 
     278                 :          7 :   event_mask |= ACTION_ADDED_EVENT;
     279                 :            : 
     280                 :          7 :   g_action_group_exporter_set_events (exporter, action_name, event_mask);
     281                 :          7 : }
     282                 :            : 
     283                 :            : static void
     284                 :          1 : g_action_group_exporter_action_removed (GActionGroup *action_group,
     285                 :            :                                         const gchar  *action_name,
     286                 :            :                                         gpointer      user_data)
     287                 :            : {
     288                 :          1 :   GActionGroupExporter *exporter = user_data;
     289                 :            :   guint event_mask;
     290                 :            : 
     291                 :          1 :   event_mask = g_action_group_exporter_get_events (exporter, action_name);
     292                 :            : 
     293                 :            :   /* If the add event for this is still queued then just cancel it since
     294                 :            :    * it's gone now.
     295                 :            :    *
     296                 :            :    * If the event was freshly added, there should not have been any
     297                 :            :    * enabled or state changed events.
     298                 :            :    */
     299         [ -  + ]:          1 :   if (event_mask & ACTION_ADDED_EVENT)
     300                 :            :     {
     301                 :          0 :       g_assert (~event_mask & ~(ACTION_STATE_CHANGED_EVENT | ACTION_ENABLED_CHANGED_EVENT));
     302                 :          0 :       event_mask &= ~ACTION_ADDED_EVENT;
     303                 :            :     }
     304                 :            : 
     305                 :            :   /* Otherwise, queue a remove event.  Drop any state or enabled changes
     306                 :            :    * that were queued before the remove. */
     307                 :            :   else
     308                 :            :     {
     309                 :          1 :       event_mask |= ACTION_REMOVED_EVENT;
     310                 :          1 :       event_mask &= ~(ACTION_STATE_CHANGED_EVENT | ACTION_ENABLED_CHANGED_EVENT);
     311                 :            :     }
     312                 :            : 
     313                 :          1 :   g_action_group_exporter_set_events (exporter, action_name, event_mask);
     314                 :          1 : }
     315                 :            : 
     316                 :            : static void
     317                 :          5 : g_action_group_exporter_action_state_changed (GActionGroup *action_group,
     318                 :            :                                               const gchar  *action_name,
     319                 :            :                                               GVariant     *value,
     320                 :            :                                               gpointer      user_data)
     321                 :            : {
     322                 :          5 :   GActionGroupExporter *exporter = user_data;
     323                 :            :   guint event_mask;
     324                 :            : 
     325                 :          5 :   event_mask = g_action_group_exporter_get_events (exporter, action_name);
     326                 :            : 
     327                 :            :   /* If it was removed, it must have been added back.  Otherwise, why
     328                 :            :    * are we hearing about changes?
     329                 :            :    */
     330                 :          5 :   g_assert (~event_mask & ACTION_REMOVED_EVENT ||
     331                 :            :             event_mask & ACTION_ADDED_EVENT);
     332                 :            : 
     333                 :            :   /* If it is freshly added, don't also bother with the state change
     334                 :            :    * signal since the updated state will be sent as part of the pending
     335                 :            :    * add message.
     336                 :            :    */
     337   [ +  -  +  - ]:          5 :   if (~event_mask & ACTION_ADDED_EVENT)
     338                 :          5 :     event_mask |= ACTION_STATE_CHANGED_EVENT;
     339                 :            : 
     340                 :          5 :   g_action_group_exporter_set_events (exporter, action_name, event_mask);
     341                 :          5 : }
     342                 :            : 
     343                 :            : static void
     344                 :      99826 : g_action_group_exporter_action_enabled_changed (GActionGroup *action_group,
     345                 :            :                                                 const gchar  *action_name,
     346                 :            :                                                 gboolean      enabled,
     347                 :            :                                                 gpointer      user_data)
     348                 :            : {
     349                 :      99826 :   GActionGroupExporter *exporter = user_data;
     350                 :            :   guint event_mask;
     351                 :            : 
     352                 :      99826 :   event_mask = g_action_group_exporter_get_events (exporter, action_name);
     353                 :            : 
     354                 :            :   /* Reasoning as above. */
     355                 :      99977 :   g_assert (~event_mask & ACTION_REMOVED_EVENT ||
     356                 :            :             event_mask & ACTION_ADDED_EVENT);
     357                 :            : 
     358   [ +  #  +  # ]:      99977 :   if (~event_mask & ACTION_ADDED_EVENT)
     359                 :      99978 :     event_mask |= ACTION_ENABLED_CHANGED_EVENT;
     360                 :            : 
     361                 :      99977 :   g_action_group_exporter_set_events (exporter, action_name, event_mask);
     362                 :      99972 : }
     363                 :            : 
     364                 :            : static void
     365                 :         17 : org_gtk_Actions_method_call (GDBusConnection       *connection,
     366                 :            :                              const gchar           *sender,
     367                 :            :                              const gchar           *object_path,
     368                 :            :                              const gchar           *interface_name,
     369                 :            :                              const gchar           *method_name,
     370                 :            :                              GVariant              *parameters,
     371                 :            :                              GDBusMethodInvocation *invocation,
     372                 :            :                              gpointer               user_data)
     373                 :            : {
     374                 :         17 :   GActionGroupExporter *exporter = user_data;
     375                 :         17 :   GVariant *result = NULL;
     376                 :            : 
     377                 :         17 :   g_action_group_exporter_flush_queue (exporter);
     378                 :            : 
     379         [ +  + ]:         17 :   if (g_str_equal (method_name, "List"))
     380                 :            :     {
     381                 :            :       gchar **list;
     382                 :            : 
     383                 :          1 :       list = g_action_group_list_actions (exporter->action_group);
     384                 :          1 :       result = g_variant_new ("(^as)", list);
     385                 :          1 :       g_strfreev (list);
     386                 :            :     }
     387                 :            : 
     388         [ +  + ]:         16 :   else if (g_str_equal (method_name, "Describe"))
     389                 :            :     {
     390                 :            :       const gchar *name;
     391                 :            :       GVariant *desc;
     392                 :            : 
     393                 :          1 :       g_variant_get (parameters, "(&s)", &name);
     394                 :            : 
     395         [ -  + ]:          1 :       if (!g_action_group_has_action (exporter->action_group, name))
     396                 :            :         {
     397                 :          0 :           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
     398                 :            :                                                  "The named action ('%s') does not exist.", name);
     399                 :          0 :           return;
     400                 :            :         }
     401                 :            : 
     402                 :          1 :       desc = g_action_group_describe_action (exporter->action_group, name);
     403                 :          1 :       result = g_variant_new ("(@(bgav))", desc);
     404                 :            :     }
     405                 :            : 
     406         [ +  + ]:         15 :   else if (g_str_equal (method_name, "DescribeAll"))
     407                 :            :     {
     408                 :            :       GVariantBuilder builder;
     409                 :            :       gchar **list;
     410                 :            :       gint i;
     411                 :            : 
     412                 :          2 :       list = g_action_group_list_actions (exporter->action_group);
     413                 :          2 :       g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{s(bgav)}"));
     414         [ +  + ]:          9 :       for (i = 0; list[i]; i++)
     415                 :            :         {
     416                 :          7 :           const gchar *name = list[i];
     417                 :            :           GVariant *description;
     418                 :            : 
     419                 :          7 :           description = g_action_group_describe_action (exporter->action_group, name);
     420                 :          7 :           g_variant_builder_add (&builder, "{s@(bgav)}", name, description);
     421                 :            :         }
     422                 :          2 :       result = g_variant_new ("(a{s(bgav)})", &builder);
     423                 :          2 :       g_strfreev (list);
     424                 :            :     }
     425                 :            : 
     426         [ +  + ]:         13 :   else if (g_str_equal (method_name, "Activate"))
     427                 :            :     {
     428                 :          8 :       GVariant *parameter = NULL;
     429                 :            :       GVariant *platform_data;
     430                 :            :       GVariantIter *iter;
     431                 :            :       const gchar *name;
     432                 :          8 :       const GVariantType *parameter_type = NULL;
     433                 :            : 
     434                 :          8 :       g_variant_get (parameters, "(&sav@a{sv})", &name, &iter, &platform_data);
     435                 :          8 :       g_variant_iter_next (iter, "v", &parameter);
     436                 :          8 :       g_variant_iter_free (iter);
     437                 :            : 
     438                 :            :       /* Check the action exists and the parameter type matches. */
     439         [ +  + ]:          8 :       if (!g_action_group_query_action (exporter->action_group,
     440                 :            :                                         name, NULL, &parameter_type,
     441                 :            :                                         NULL, NULL, NULL))
     442                 :            :         {
     443                 :          1 :           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
     444                 :            :                                                  "Unknown action ‘%s’", name);
     445                 :          1 :           g_clear_pointer (&parameter, g_variant_unref);
     446                 :          1 :           g_variant_unref (platform_data);
     447                 :          4 :           return;
     448                 :            :         }
     449                 :            : 
     450   [ +  +  +  + ]:          7 :       if (!((parameter_type == NULL && parameter == NULL) ||
     451   [ +  +  +  +  :          4 :             (parameter_type != NULL && parameter != NULL && g_variant_is_of_type (parameter, parameter_type))))
                   +  + ]
     452                 :            :         {
     453                 :          4 :           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
     454                 :            :                                                  "Invalid parameter for action ‘%s’: expected type %s but got type %s",
     455                 :            :                                                  name,
     456         [ +  + ]:          3 :                                                  (parameter_type != NULL) ? (const gchar *) parameter_type : "()",
     457         [ +  + ]:          3 :                                                  (parameter != NULL) ? g_variant_get_type_string (parameter) : "()");
     458                 :          3 :           g_clear_pointer (&parameter, g_variant_unref);
     459                 :          3 :           g_variant_unref (platform_data);
     460                 :          3 :           return;
     461                 :            :         }
     462                 :            : 
     463   [ -  +  +  -  :          4 :       if (G_IS_REMOTE_ACTION_GROUP (exporter->action_group))
             -  +  -  + ]
     464                 :          0 :         g_remote_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (exporter->action_group),
     465                 :            :                                                     name, parameter, platform_data);
     466                 :            :       else
     467                 :          4 :         g_action_group_activate_action (exporter->action_group, name, parameter);
     468                 :            : 
     469         [ +  + ]:          4 :       if (parameter)
     470                 :          1 :         g_variant_unref (parameter);
     471                 :            : 
     472                 :          4 :       g_variant_unref (platform_data);
     473                 :            :     }
     474                 :            : 
     475         [ +  - ]:          5 :   else if (g_str_equal (method_name, "SetState"))
     476                 :            :     {
     477                 :            :       GVariant *platform_data;
     478                 :            :       const gchar *name;
     479                 :            :       GVariant *state;
     480                 :          5 :       const GVariantType *state_type = NULL;
     481                 :            : 
     482                 :          5 :       g_variant_get (parameters, "(&sv@a{sv})", &name, &state, &platform_data);
     483                 :            : 
     484                 :            :       /* Check the action exists and the state type matches. */
     485         [ +  + ]:          5 :       if (!g_action_group_query_action (exporter->action_group,
     486                 :            :                                         name, NULL, NULL,
     487                 :            :                                         &state_type, NULL, NULL))
     488                 :            :         {
     489                 :          1 :           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
     490                 :            :                                                  "Unknown action ‘%s’", name);
     491                 :          1 :           g_variant_unref (state);
     492                 :          1 :           g_variant_unref (platform_data);
     493                 :          3 :           return;
     494                 :            :         }
     495                 :            : 
     496         [ +  + ]:          4 :       if (state_type == NULL)
     497                 :            :         {
     498                 :          1 :           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
     499                 :            :                                                  "Cannot change state of action ‘%s’ as it is stateless", name);
     500                 :          1 :           g_variant_unref (state);
     501                 :          1 :           g_variant_unref (platform_data);
     502                 :          1 :           return;
     503                 :            :         }
     504                 :            : 
     505         [ +  + ]:          3 :       if (!g_variant_is_of_type (state, state_type))
     506                 :            :         {
     507                 :          1 :           g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
     508                 :            :                                                  "Invalid state for action ‘%s’: expected type %s but got type %s",
     509                 :            :                                                  name,
     510                 :            :                                                  (const gchar *) state_type,
     511                 :            :                                                  g_variant_get_type_string (state));
     512                 :          1 :           g_variant_unref (state);
     513                 :          1 :           g_variant_unref (platform_data);
     514                 :          1 :           return;
     515                 :            :         }
     516                 :            : 
     517   [ -  +  +  -  :          2 :       if (G_IS_REMOTE_ACTION_GROUP (exporter->action_group))
             -  +  -  + ]
     518                 :          0 :         g_remote_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (exporter->action_group),
     519                 :            :                                                         name, state, platform_data);
     520                 :            :       else
     521                 :          2 :         g_action_group_change_action_state (exporter->action_group, name, state);
     522                 :            : 
     523                 :          2 :       g_variant_unref (platform_data);
     524                 :          2 :       g_variant_unref (state);
     525                 :            :     }
     526                 :            : 
     527                 :            :   else
     528                 :            :     g_assert_not_reached ();
     529                 :            : 
     530                 :         10 :   g_dbus_method_invocation_return_value (invocation, result);
     531                 :            : }
     532                 :            : 
     533                 :            : static void
     534                 :     100030 : g_action_group_exporter_free (gpointer user_data)
     535                 :            : {
     536                 :     100030 :   GActionGroupExporter *exporter = user_data;
     537                 :            : 
     538                 :     100030 :   g_signal_handlers_disconnect_by_func (exporter->action_group,
     539                 :            :                                         g_action_group_exporter_action_added, exporter);
     540                 :     100011 :   g_signal_handlers_disconnect_by_func (exporter->action_group,
     541                 :            :                                         g_action_group_exporter_action_enabled_changed, exporter);
     542                 :     100008 :   g_signal_handlers_disconnect_by_func (exporter->action_group,
     543                 :            :                                         g_action_group_exporter_action_state_changed, exporter);
     544                 :     100002 :   g_signal_handlers_disconnect_by_func (exporter->action_group,
     545                 :            :                                         g_action_group_exporter_action_removed, exporter);
     546                 :            : 
     547                 :     100012 :   g_hash_table_unref (exporter->pending_changes);
     548         [ +  + ]:     100016 :   if (exporter->pending_source)
     549                 :      99985 :     g_source_destroy (exporter->pending_source);
     550                 :            : 
     551                 :     100007 :   g_main_context_unref (exporter->context);
     552                 :     100009 :   g_object_unref (exporter->connection);
     553                 :     100017 :   g_object_unref (exporter->action_group);
     554                 :      99990 :   g_free (exporter->object_path);
     555                 :            : 
     556                 :     100014 :   g_slice_free (GActionGroupExporter, exporter);
     557                 :     100016 : }
     558                 :            : 
     559                 :            : /**
     560                 :            :  * g_dbus_connection_export_action_group:
     561                 :            :  * @connection: a #GDBusConnection
     562                 :            :  * @object_path: a D-Bus object path
     563                 :            :  * @action_group: a #GActionGroup
     564                 :            :  * @error: a pointer to a %NULL #GError, or %NULL
     565                 :            :  *
     566                 :            :  * Exports @action_group on @connection at @object_path.
     567                 :            :  *
     568                 :            :  * The implemented D-Bus API should be considered private.  It is
     569                 :            :  * subject to change in the future.
     570                 :            :  *
     571                 :            :  * A given object path can only have one action group exported on it.
     572                 :            :  * If this constraint is violated, the export will fail and 0 will be
     573                 :            :  * returned (with @error set accordingly).
     574                 :            :  *
     575                 :            :  * You can unexport the action group using
     576                 :            :  * g_dbus_connection_unexport_action_group() with the return value of
     577                 :            :  * this function.
     578                 :            :  *
     579                 :            :  * The thread default main context is taken at the time of this call.
     580                 :            :  * All incoming action activations and state change requests are
     581                 :            :  * reported from this context.  Any changes on the action group that
     582                 :            :  * cause it to emit signals must also come from this same context.
     583                 :            :  * Since incoming action activations and state change requests are
     584                 :            :  * rather likely to cause changes on the action group, this effectively
     585                 :            :  * limits a given action group to being exported from only one main
     586                 :            :  * context.
     587                 :            :  *
     588                 :            :  * Returns: the ID of the export (never zero), or 0 in case of failure
     589                 :            :  *
     590                 :            :  * Since: 2.32
     591                 :            :  **/
     592                 :            : guint
     593                 :      99949 : g_dbus_connection_export_action_group (GDBusConnection  *connection,
     594                 :            :                                        const gchar      *object_path,
     595                 :            :                                        GActionGroup     *action_group,
     596                 :            :                                        GError          **error)
     597                 :            : {
     598                 :      99949 :   const GDBusInterfaceVTable vtable = {
     599                 :            :     org_gtk_Actions_method_call, NULL, NULL, { 0 }
     600                 :            :   };
     601                 :            :   GActionGroupExporter *exporter;
     602                 :            :   guint id;
     603                 :            : 
     604         [ +  + ]:      99949 :   if G_UNLIKELY (org_gtk_Actions == NULL)
     605                 :            :     {
     606                 :          7 :       GError *my_error = NULL;
     607                 :            :       GDBusNodeInfo *info;
     608                 :            : 
     609                 :          7 :       info = g_dbus_node_info_new_for_xml (org_gtk_Actions_xml, &my_error);
     610         [ -  + ]:          7 :       if G_UNLIKELY (info == NULL)
     611                 :          0 :         g_error ("%s", my_error->message);
     612                 :          7 :       org_gtk_Actions = g_dbus_node_info_lookup_interface (info, "org.gtk.Actions");
     613                 :          7 :       g_assert (org_gtk_Actions != NULL);
     614                 :          7 :       g_dbus_interface_info_ref (org_gtk_Actions);
     615                 :          7 :       g_dbus_node_info_unref (info);
     616                 :            :     }
     617                 :            : 
     618                 :      99949 :   exporter = g_slice_new (GActionGroupExporter);
     619                 :     100006 :   id = g_dbus_connection_register_object (connection, object_path, org_gtk_Actions, &vtable,
     620                 :            :                                           exporter, g_action_group_exporter_free, error);
     621                 :            : 
     622         [ -  + ]:     100029 :   if (id == 0)
     623                 :            :     {
     624                 :          0 :       g_slice_free (GActionGroupExporter, exporter);
     625                 :          0 :       return 0;
     626                 :            :     }
     627                 :            : 
     628                 :     100029 :   exporter->context = g_main_context_ref_thread_default ();
     629                 :     100031 :   exporter->pending_changes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
     630                 :     100028 :   exporter->pending_source = NULL;
     631                 :     100028 :   exporter->action_group = g_object_ref (action_group);
     632                 :     100027 :   exporter->connection = g_object_ref (connection);
     633                 :     100031 :   exporter->object_path = g_strdup (object_path);
     634                 :            : 
     635                 :     100031 :   g_signal_connect (action_group, "action-added",
     636                 :            :                     G_CALLBACK (g_action_group_exporter_action_added), exporter);
     637                 :     100029 :   g_signal_connect (action_group, "action-removed",
     638                 :            :                     G_CALLBACK (g_action_group_exporter_action_removed), exporter);
     639                 :     100028 :   g_signal_connect (action_group, "action-state-changed",
     640                 :            :                     G_CALLBACK (g_action_group_exporter_action_state_changed), exporter);
     641                 :     100031 :   g_signal_connect (action_group, "action-enabled-changed",
     642                 :            :                     G_CALLBACK (g_action_group_exporter_action_enabled_changed), exporter);
     643                 :            : 
     644                 :     100031 :   return id;
     645                 :            : }
     646                 :            : 
     647                 :            : /**
     648                 :            :  * g_dbus_connection_unexport_action_group:
     649                 :            :  * @connection: a #GDBusConnection
     650                 :            :  * @export_id: the ID from g_dbus_connection_export_action_group()
     651                 :            :  *
     652                 :            :  * Reverses the effect of a previous call to
     653                 :            :  * g_dbus_connection_export_action_group().
     654                 :            :  *
     655                 :            :  * It is an error to call this function with an ID that wasn't returned
     656                 :            :  * from g_dbus_connection_export_action_group() or to call it with the
     657                 :            :  * same ID more than once.
     658                 :            :  *
     659                 :            :  * Since: 2.32
     660                 :            :  **/
     661                 :            : void
     662                 :      99880 : g_dbus_connection_unexport_action_group (GDBusConnection *connection,
     663                 :            :                                          guint            export_id)
     664                 :            : {
     665                 :      99880 :   g_dbus_connection_unregister_object (connection, export_id);
     666                 :     100031 : }

Generated by: LCOV version 1.14