LCOV - code coverage report
Current view: top level - gio - gactiongroupexporter.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 94.0 % 215 202
Test Date: 2024-11-26 05:23:01 Functions: 100.0 % 13 13
Branches: - 0 0

             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 [iface@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_static (&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_static (&removes, G_VARIANT_TYPE_STRING_ARRAY);
     150                 :          10 :   g_variant_builder_init_static (&enabled_changes, G_VARIANT_TYPE ("a{sb}"));
     151                 :          10 :   g_variant_builder_init_static (&state_changes, G_VARIANT_TYPE ("a{sv}"));
     152                 :          10 :   g_variant_builder_init_static (&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                 :      100014 : g_action_group_exporter_get_events (GActionGroupExporter *exporter,
     222                 :             :                                     const gchar          *name)
     223                 :             : {
     224                 :      100014 :   return (gsize) g_hash_table_lookup (exporter->pending_changes, name);
     225                 :             : }
     226                 :             : 
     227                 :             : static void
     228                 :      100014 : 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                 :      100014 :   if (events != 0)
     236                 :      200028 :     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                 :      100014 :   have_events = g_hash_table_size (exporter->pending_changes) > 0;
     241                 :      100014 :   is_queued = exporter->pending_source != NULL;
     242                 :             : 
     243                 :      100014 :   if (have_events && !is_queued)
     244                 :             :     {
     245                 :             :       GSource *source;
     246                 :             : 
     247                 :      100010 :       source = g_idle_source_new ();
     248                 :      100010 :       exporter->pending_source = source;
     249                 :      100010 :       g_source_set_callback (source, g_action_group_exporter_dispatch_events, exporter, NULL);
     250                 :      100010 :       g_source_set_static_name (source, "[gio] g_action_group_exporter_dispatch_events");
     251                 :      100010 :       g_source_attach (source, exporter->context);
     252                 :      100010 :       g_source_unref (source);
     253                 :             :     }
     254                 :             : 
     255                 :      100014 :   if (!have_events && is_queued)
     256                 :             :     {
     257                 :           0 :       g_source_destroy (exporter->pending_source);
     258                 :           0 :       exporter->pending_source = NULL;
     259                 :             :     }
     260                 :      100014 : }
     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                 :      100001 : g_action_group_exporter_action_enabled_changed (GActionGroup *action_group,
     345                 :             :                                                 const gchar  *action_name,
     346                 :             :                                                 gboolean      enabled,
     347                 :             :                                                 gpointer      user_data)
     348                 :             : {
     349                 :      100001 :   GActionGroupExporter *exporter = user_data;
     350                 :             :   guint event_mask;
     351                 :             : 
     352                 :      100001 :   event_mask = g_action_group_exporter_get_events (exporter, action_name);
     353                 :             : 
     354                 :             :   /* Reasoning as above. */
     355                 :      100001 :   g_assert (~event_mask & ACTION_REMOVED_EVENT ||
     356                 :             :             event_mask & ACTION_ADDED_EVENT);
     357                 :             : 
     358                 :      100001 :   if (~event_mask & ACTION_ADDED_EVENT)
     359                 :      100001 :     event_mask |= ACTION_ENABLED_CHANGED_EVENT;
     360                 :             : 
     361                 :      100001 :   g_action_group_exporter_set_events (exporter, action_name, event_mask);
     362                 :      100001 : }
     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_static (&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                 :      100039 : g_action_group_exporter_free (GActionGroupExporter *exporter)
     535                 :             : {
     536                 :      100039 :   g_signal_handlers_disconnect_by_func (exporter->action_group,
     537                 :             :                                         g_action_group_exporter_action_added, exporter);
     538                 :      100039 :   g_signal_handlers_disconnect_by_func (exporter->action_group,
     539                 :             :                                         g_action_group_exporter_action_enabled_changed, exporter);
     540                 :      100039 :   g_signal_handlers_disconnect_by_func (exporter->action_group,
     541                 :             :                                         g_action_group_exporter_action_state_changed, exporter);
     542                 :      100039 :   g_signal_handlers_disconnect_by_func (exporter->action_group,
     543                 :             :                                         g_action_group_exporter_action_removed, exporter);
     544                 :             : 
     545                 :      100039 :   g_hash_table_unref (exporter->pending_changes);
     546                 :      100039 :   if (exporter->pending_source)
     547                 :      100000 :     g_source_destroy (exporter->pending_source);
     548                 :             : 
     549                 :      100039 :   g_main_context_unref (exporter->context);
     550                 :      100039 :   g_object_unref (exporter->connection);
     551                 :      100039 :   g_object_unref (exporter->action_group);
     552                 :      100039 :   g_free (exporter->object_path);
     553                 :             : 
     554                 :      100039 :   g_slice_free (GActionGroupExporter, exporter);
     555                 :      100039 : }
     556                 :             : 
     557                 :             : /**
     558                 :             :  * g_dbus_connection_export_action_group:
     559                 :             :  * @connection: the D-Bus connection
     560                 :             :  * @object_path: a D-Bus object path
     561                 :             :  * @action_group: an action group
     562                 :             :  *
     563                 :             :  * Exports @action_group on @connection at @object_path.
     564                 :             :  *
     565                 :             :  * The implemented D-Bus API should be considered private.  It is
     566                 :             :  * subject to change in the future.
     567                 :             :  *
     568                 :             :  * A given object path can only have one action group exported on it.
     569                 :             :  * If this constraint is violated, the export will fail and 0 will be
     570                 :             :  * returned (with @error set accordingly).
     571                 :             :  *
     572                 :             :  * You can unexport the action group using
     573                 :             :  * [method@Gio.DBusConnection.unexport_action_group] with the return value of
     574                 :             :  * this function.
     575                 :             :  *
     576                 :             :  * The thread default main context is taken at the time of this call.
     577                 :             :  * All incoming action activations and state change requests are
     578                 :             :  * reported from this context.  Any changes on the action group that
     579                 :             :  * cause it to emit signals must also come from this same context.
     580                 :             :  * Since incoming action activations and state change requests are
     581                 :             :  * rather likely to cause changes on the action group, this effectively
     582                 :             :  * limits a given action group to being exported from only one main
     583                 :             :  * context.
     584                 :             :  *
     585                 :             :  * Returns: the ID of the export (never zero), or 0 in case of failure
     586                 :             :  *
     587                 :             :  * Since: 2.32
     588                 :             :  **/
     589                 :             : guint
     590                 :      100039 : g_dbus_connection_export_action_group (GDBusConnection  *connection,
     591                 :             :                                        const gchar      *object_path,
     592                 :             :                                        GActionGroup     *action_group,
     593                 :             :                                        GError          **error)
     594                 :             : {
     595                 :      100039 :   const GDBusInterfaceVTable vtable = {
     596                 :             :     org_gtk_Actions_method_call, NULL, NULL, { 0 }
     597                 :             :   };
     598                 :             :   GActionGroupExporter *exporter;
     599                 :             :   guint id;
     600                 :             : 
     601                 :      100039 :   if G_UNLIKELY (org_gtk_Actions == NULL)
     602                 :             :     {
     603                 :           7 :       GError *my_error = NULL;
     604                 :             :       GDBusNodeInfo *info;
     605                 :             : 
     606                 :           7 :       info = g_dbus_node_info_new_for_xml (org_gtk_Actions_xml, &my_error);
     607                 :           7 :       if G_UNLIKELY (info == NULL)
     608                 :           0 :         g_error ("%s", my_error->message);
     609                 :           7 :       org_gtk_Actions = g_dbus_node_info_lookup_interface (info, "org.gtk.Actions");
     610                 :           7 :       g_assert (org_gtk_Actions != NULL);
     611                 :           7 :       g_dbus_interface_info_ref (org_gtk_Actions);
     612                 :           7 :       g_dbus_node_info_unref (info);
     613                 :             :     }
     614                 :             : 
     615                 :      100039 :   exporter = g_slice_new (GActionGroupExporter);
     616                 :      100039 :   exporter->context = g_main_context_ref_thread_default ();
     617                 :      100039 :   exporter->pending_changes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
     618                 :      100039 :   exporter->pending_source = NULL;
     619                 :      100039 :   exporter->action_group = g_object_ref (action_group);
     620                 :      100039 :   exporter->connection = g_object_ref (connection);
     621                 :      100039 :   exporter->object_path = g_strdup (object_path);
     622                 :             : 
     623                 :      100039 :   id = g_dbus_connection_register_object (connection, object_path, org_gtk_Actions, &vtable,
     624                 :             :                                           exporter, (GDestroyNotify) g_action_group_exporter_free, error);
     625                 :             : 
     626                 :      100039 :   if (id != 0)
     627                 :             :     {
     628                 :      100038 :       g_signal_connect (action_group, "action-added",
     629                 :             :                         G_CALLBACK (g_action_group_exporter_action_added), exporter);
     630                 :      100038 :       g_signal_connect (action_group, "action-removed",
     631                 :             :                         G_CALLBACK (g_action_group_exporter_action_removed), exporter);
     632                 :      100038 :       g_signal_connect (action_group, "action-state-changed",
     633                 :             :                         G_CALLBACK (g_action_group_exporter_action_state_changed), exporter);
     634                 :      100038 :       g_signal_connect (action_group, "action-enabled-changed",
     635                 :             :                         G_CALLBACK (g_action_group_exporter_action_enabled_changed), exporter);
     636                 :             :     }
     637                 :             : 
     638                 :      100039 :   return id;
     639                 :             : }
     640                 :             : 
     641                 :             : /**
     642                 :             :  * g_dbus_connection_unexport_action_group:
     643                 :             :  * @connection: the D-Bus connection
     644                 :             :  * @export_id: the ID from [method@Gio.DBusConnection.export_action_group]
     645                 :             :  *
     646                 :             :  * Reverses the effect of a previous call to
     647                 :             :  * [method@Gio.DBusConnection.export_action_group].
     648                 :             :  *
     649                 :             :  * It is an error to call this function with an ID that wasn’t returned from
     650                 :             :  * [method@Gio.DBusConnection.export_action_group] or to call it with the same
     651                 :             :  * ID more than once.
     652                 :             :  *
     653                 :             :  * Since: 2.32
     654                 :             :  **/
     655                 :             : void
     656                 :      100038 : g_dbus_connection_unexport_action_group (GDBusConnection *connection,
     657                 :             :                                          guint            export_id)
     658                 :             : {
     659                 :      100038 :   g_dbus_connection_unregister_object (connection, export_id);
     660                 :      100038 : }
        

Generated by: LCOV version 2.0-1