LCOV - code coverage report
Current view: top level - glib/gobject - gsignalgroup.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 277 284 97.5 %
Date: 2024-06-11 05:24:59 Functions: 31 31 100.0 %
Branches: 67 76 88.2 %

           Branch data     Line data    Source code
       1                 :            : /* GObject - GLib Type, Object, Parameter and Signal Library
       2                 :            :  *
       3                 :            :  * Copyright (C) 2015-2022 Christian Hergert <christian@hergert.me>
       4                 :            :  * Copyright (C) 2015 Garrett Regier <garrettregier@gmail.com>
       5                 :            :  *
       6                 :            :  * This library is free software; you can redistribute it and/or
       7                 :            :  * modify it under the terms of the GNU Lesser General Public
       8                 :            :  * License as published by the Free Software Foundation; either
       9                 :            :  * version 2.1 of the License, or (at your option) any later version.
      10                 :            :  *
      11                 :            :  * This library is distributed in the hope that it will be useful,
      12                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14                 :            :  * Lesser General Public License for more details.
      15                 :            :  *
      16                 :            :  * You should have received a copy of the GNU Lesser General
      17                 :            :  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18                 :            :  *
      19                 :            :  * SPDX-License-Identifier: LGPL-2.1-or-later
      20                 :            :  */
      21                 :            : 
      22                 :            : #include "config.h"
      23                 :            : #include "glib.h"
      24                 :            : #include "glibintl.h"
      25                 :            : 
      26                 :            : #include "gparamspecs.h"
      27                 :            : #include "gsignalgroup.h"
      28                 :            : #include "gvaluetypes.h"
      29                 :            : 
      30                 :            : /**
      31                 :            :  * GSignalGroup:
      32                 :            :  *
      33                 :            :  * `GSignalGroup` manages a collection of signals on a `GObject`.
      34                 :            :  *
      35                 :            :  * `GSignalGroup` simplifies the process of connecting  many signals to a `GObject`
      36                 :            :  * as a group. As such there is no API to disconnect a signal from the group.
      37                 :            :  *
      38                 :            :  * In particular, this allows you to:
      39                 :            :  *
      40                 :            :  *  - Change the target instance, which automatically causes disconnection
      41                 :            :  *    of the signals from the old instance and connecting to the new instance.
      42                 :            :  *  - Block and unblock signals as a group
      43                 :            :  *  - Ensuring that blocked state transfers across target instances.
      44                 :            :  *
      45                 :            :  * One place you might want to use such a structure is with `GtkTextView` and
      46                 :            :  * `GtkTextBuffer`. Often times, you'll need to connect to many signals on
      47                 :            :  * `GtkTextBuffer` from a `GtkTextView` subclass. This allows you to create a
      48                 :            :  * signal group during instance construction, simply bind the
      49                 :            :  * `GtkTextView:buffer` property to `GSignalGroup:target` and connect
      50                 :            :  * all the signals you need. When the `GtkTextView:buffer` property changes
      51                 :            :  * all of the signals will be transitioned correctly.
      52                 :            :  *
      53                 :            :  * Since: 2.72
      54                 :            :  */
      55                 :            : 
      56                 :            : struct _GSignalGroup
      57                 :            : {
      58                 :            :   GObject     parent_instance;
      59                 :            : 
      60                 :            :   GWeakRef    target_ref;
      61                 :            :   GRecMutex   mutex;
      62                 :            :   GPtrArray  *handlers;
      63                 :            :   GType       target_type;
      64                 :            :   gssize      block_count;
      65                 :            : 
      66                 :            :   guint       has_bound_at_least_once : 1;
      67                 :            : };
      68                 :            : 
      69                 :            : typedef struct _GSignalGroupClass
      70                 :            : {
      71                 :            :   GObjectClass parent_class;
      72                 :            : 
      73                 :            :   void (*bind) (GSignalGroup *self,
      74                 :            :                 GObject      *target);
      75                 :            : } GSignalGroupClass;
      76                 :            : 
      77                 :            : typedef struct
      78                 :            : {
      79                 :            :   GSignalGroup *group;
      80                 :            :   gulong             handler_id;
      81                 :            :   GClosure          *closure;
      82                 :            :   guint              signal_id;
      83                 :            :   GQuark             signal_detail;
      84                 :            :   guint              connect_after : 1;
      85                 :            : } SignalHandler;
      86                 :            : 
      87   [ +  +  +  -  :        452 : G_DEFINE_TYPE (GSignalGroup, g_signal_group, G_TYPE_OBJECT)
                   +  + ]
      88                 :            : 
      89                 :            : typedef enum
      90                 :            : {
      91                 :            :   PROP_TARGET = 1,
      92                 :            :   PROP_TARGET_TYPE,
      93                 :            :   LAST_PROP
      94                 :            : } GSignalGroupProperty;
      95                 :            : 
      96                 :            : enum
      97                 :            : {
      98                 :            :   BIND,
      99                 :            :   UNBIND,
     100                 :            :   LAST_SIGNAL
     101                 :            : };
     102                 :            : 
     103                 :            : static GParamSpec *properties[LAST_PROP];
     104                 :            : static guint signals[LAST_SIGNAL];
     105                 :            : 
     106                 :            : static void
     107                 :         14 : g_signal_group_set_target_type (GSignalGroup *self,
     108                 :            :                                 GType         target_type)
     109                 :            : {
     110                 :         14 :   g_assert (G_IS_SIGNAL_GROUP (self));
     111                 :         14 :   g_assert (g_type_is_a (target_type, G_TYPE_OBJECT));
     112                 :            : 
     113                 :         14 :   self->target_type = target_type;
     114                 :            : 
     115                 :            :   /* The class must be created at least once for the signals
     116                 :            :    * to be registered, otherwise g_signal_parse_name() will fail
     117                 :            :    */
     118         [ +  + ]:         14 :   if (G_TYPE_IS_INTERFACE (target_type))
     119                 :            :     {
     120         [ +  - ]:          1 :       if (g_type_default_interface_peek (target_type) == NULL)
     121                 :          1 :         g_type_default_interface_unref (g_type_default_interface_ref (target_type));
     122                 :            :     }
     123                 :            :   else
     124                 :            :     {
     125         [ +  + ]:         13 :       if (g_type_class_peek (target_type) == NULL)
     126                 :          1 :         g_type_class_unref (g_type_class_ref (target_type));
     127                 :            :     }
     128                 :         14 : }
     129                 :            : 
     130                 :            : static void
     131                 :         67 : g_signal_group_gc_handlers (GSignalGroup *self)
     132                 :            : {
     133                 :            :   guint i;
     134                 :            : 
     135                 :         67 :   g_assert (G_IS_SIGNAL_GROUP (self));
     136                 :            : 
     137                 :            :   /*
     138                 :            :    * Remove any handlers for which the closures have become invalid. We do
     139                 :            :    * this cleanup lazily to avoid situations where we could have disposal
     140                 :            :    * active on both the signal group and the peer object.
     141                 :            :    */
     142                 :            : 
     143         [ +  + ]:        313 :   for (i = self->handlers->len; i > 0; i--)
     144                 :            :     {
     145                 :        246 :       const SignalHandler *handler = g_ptr_array_index (self->handlers, i - 1);
     146                 :            : 
     147                 :        246 :       g_assert (handler != NULL);
     148                 :        246 :       g_assert (handler->closure != NULL);
     149                 :            : 
     150         [ +  + ]:        246 :       if (handler->closure->is_invalid)
     151                 :          4 :         g_ptr_array_remove_index (self->handlers, i - 1);
     152                 :            :     }
     153                 :         67 : }
     154                 :            : 
     155                 :            : static void
     156                 :          5 : g_signal_group__target_weak_notify (gpointer  data,
     157                 :            :                                     GObject  *where_object_was)
     158                 :            : {
     159                 :          5 :   GSignalGroup *self = data;
     160                 :            :   guint i;
     161                 :            : 
     162                 :          5 :   g_assert (G_IS_SIGNAL_GROUP (self));
     163                 :          5 :   g_assert (where_object_was != NULL);
     164                 :            : 
     165                 :          5 :   g_rec_mutex_lock (&self->mutex);
     166                 :            : 
     167                 :          5 :   g_weak_ref_set (&self->target_ref, NULL);
     168                 :            : 
     169         [ +  + ]:         29 :   for (i = 0; i < self->handlers->len; i++)
     170                 :            :     {
     171                 :         24 :       SignalHandler *handler = g_ptr_array_index (self->handlers, i);
     172                 :            : 
     173                 :         24 :       handler->handler_id = 0;
     174                 :            :     }
     175                 :            : 
     176                 :          5 :   g_signal_emit (self, signals[UNBIND], 0);
     177                 :          5 :   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TARGET]);
     178                 :            : 
     179                 :          5 :   g_rec_mutex_unlock (&self->mutex);
     180                 :          5 : }
     181                 :            : 
     182                 :            : static void
     183                 :         41 : g_signal_group_bind_handler (GSignalGroup  *self,
     184                 :            :                              SignalHandler *handler,
     185                 :            :                              GObject       *target)
     186                 :            : {
     187                 :            :   gssize i;
     188                 :            : 
     189                 :         41 :   g_assert (self != NULL);
     190                 :         41 :   g_assert (G_IS_OBJECT (target));
     191                 :         41 :   g_assert (handler != NULL);
     192                 :         41 :   g_assert (handler->signal_id != 0);
     193                 :         41 :   g_assert (handler->closure != NULL);
     194                 :         41 :   g_assert (handler->closure->is_invalid == 0);
     195                 :         41 :   g_assert (handler->handler_id == 0);
     196                 :            : 
     197                 :         82 :   handler->handler_id = g_signal_connect_closure_by_id (target,
     198                 :            :                                                         handler->signal_id,
     199                 :            :                                                         handler->signal_detail,
     200                 :            :                                                         handler->closure,
     201                 :         41 :                                                         handler->connect_after);
     202                 :            : 
     203                 :         41 :   g_assert (handler->handler_id != 0);
     204                 :            : 
     205         [ +  + ]:         57 :   for (i = 0; i < self->block_count; i++)
     206                 :         16 :     g_signal_handler_block (target, handler->handler_id);
     207                 :         41 : }
     208                 :            : 
     209                 :            : static void
     210                 :         12 : g_signal_group_bind (GSignalGroup *self,
     211                 :            :                      GObject      *target)
     212                 :            : {
     213                 :            :   GObject *hold;
     214                 :            :   guint i;
     215                 :            : 
     216                 :         12 :   g_assert (G_IS_SIGNAL_GROUP (self));
     217                 :         12 :   g_assert (!target || G_IS_OBJECT (target));
     218                 :            : 
     219         [ +  + ]:         12 :   if (target == NULL)
     220                 :          1 :     return;
     221                 :            : 
     222                 :         11 :   self->has_bound_at_least_once = TRUE;
     223                 :            : 
     224                 :         11 :   hold = g_object_ref (target);
     225                 :            : 
     226                 :         11 :   g_weak_ref_set (&self->target_ref, hold);
     227                 :         11 :   g_object_weak_ref (hold, g_signal_group__target_weak_notify, self);
     228                 :            : 
     229                 :         11 :   g_signal_group_gc_handlers (self);
     230                 :            : 
     231         [ +  + ]:         52 :   for (i = 0; i < self->handlers->len; i++)
     232                 :            :     {
     233                 :         41 :       SignalHandler *handler = g_ptr_array_index (self->handlers, i);
     234                 :            : 
     235                 :         41 :       g_signal_group_bind_handler (self, handler, hold);
     236                 :            :     }
     237                 :            : 
     238                 :         11 :   g_signal_emit (self, signals [BIND], 0, hold);
     239                 :            : 
     240                 :         11 :   g_object_unref (hold);
     241                 :            : }
     242                 :            : 
     243                 :            : static void
     244                 :         12 : g_signal_group_unbind (GSignalGroup *self)
     245                 :            : {
     246                 :            :   GObject *target;
     247                 :            :   guint i;
     248                 :            : 
     249                 :         12 :   g_return_if_fail (G_IS_SIGNAL_GROUP (self));
     250                 :            : 
     251                 :         12 :   target = g_weak_ref_get (&self->target_ref);
     252                 :            : 
     253                 :            :   /*
     254                 :            :    * Target may be NULL by this point, as we got notified of its destruction.
     255                 :            :    * However, if we're early enough, we may get a full reference back and can
     256                 :            :    * cleanly disconnect our connections.
     257                 :            :    */
     258                 :            : 
     259         [ +  + ]:         12 :   if (target != NULL)
     260                 :            :     {
     261                 :          6 :       g_weak_ref_set (&self->target_ref, NULL);
     262                 :            : 
     263                 :            :       /*
     264                 :            :        * Let go of our weak reference now that we have a full reference
     265                 :            :        * for the life of this function.
     266                 :            :        */
     267                 :          6 :       g_object_weak_unref (target,
     268                 :            :                            g_signal_group__target_weak_notify,
     269                 :            :                            self);
     270                 :            :     }
     271                 :            : 
     272                 :         12 :   g_signal_group_gc_handlers (self);
     273                 :            : 
     274         [ +  + ]:         52 :   for (i = 0; i < self->handlers->len; i++)
     275                 :            :     {
     276                 :            :       SignalHandler *handler;
     277                 :            :       gulong handler_id;
     278                 :            : 
     279                 :         40 :       handler = g_ptr_array_index (self->handlers, i);
     280                 :            : 
     281                 :         40 :       g_assert (handler != NULL);
     282                 :         40 :       g_assert (handler->signal_id != 0);
     283                 :         40 :       g_assert (handler->closure != NULL);
     284                 :            : 
     285                 :         40 :       handler_id = handler->handler_id;
     286                 :         40 :       handler->handler_id = 0;
     287                 :            : 
     288                 :            :       /*
     289                 :            :        * If @target is NULL, we lost a race to cleanup the weak
     290                 :            :        * instance and the signal connections have already been
     291                 :            :        * finalized and therefore nothing to do.
     292                 :            :        */
     293                 :            : 
     294   [ +  +  +  - ]:         40 :       if (target != NULL && handler_id != 0)
     295                 :         16 :         g_signal_handler_disconnect (target, handler_id);
     296                 :            :     }
     297                 :            : 
     298                 :         12 :   g_signal_emit (self, signals [UNBIND], 0);
     299                 :            : 
     300                 :         12 :   g_clear_object (&target);
     301                 :            : }
     302                 :            : 
     303                 :            : static gboolean
     304                 :         27 : g_signal_group_check_target_type (GSignalGroup *self,
     305                 :            :                                   gpointer      target)
     306                 :            : {
     307         [ +  + ]:         27 :   if ((target != NULL) &&
     308   [ +  +  +  - ]:         12 :       !g_type_is_a (G_OBJECT_TYPE (target), self->target_type))
     309                 :            :     {
     310                 :          1 :       g_critical ("Failed to set GSignalGroup of target type %s "
     311                 :            :                   "using target %p of type %s",
     312                 :            :                   g_type_name (self->target_type),
     313                 :            :                   target, G_OBJECT_TYPE_NAME (target));
     314                 :          1 :       return FALSE;
     315                 :            :     }
     316                 :            : 
     317                 :         26 :   return TRUE;
     318                 :            : }
     319                 :            : 
     320                 :            : /**
     321                 :            :  * g_signal_group_block:
     322                 :            :  * @self: the #GSignalGroup
     323                 :            :  *
     324                 :            :  * Blocks all signal handlers managed by @self so they will not
     325                 :            :  * be called during any signal emissions. Must be unblocked exactly
     326                 :            :  * the same number of times it has been blocked to become active again.
     327                 :            :  *
     328                 :            :  * This blocked state will be kept across changes of the target instance.
     329                 :            :  *
     330                 :            :  * Since: 2.72
     331                 :            :  */
     332                 :            : void
     333                 :         22 : g_signal_group_block (GSignalGroup *self)
     334                 :            : {
     335                 :            :   GObject *target;
     336                 :            :   guint i;
     337                 :            : 
     338                 :         22 :   g_return_if_fail (G_IS_SIGNAL_GROUP (self));
     339                 :         22 :   g_return_if_fail (self->block_count >= 0);
     340                 :            : 
     341                 :         22 :   g_rec_mutex_lock (&self->mutex);
     342                 :            : 
     343                 :         22 :   self->block_count++;
     344                 :            : 
     345                 :         22 :   target = g_weak_ref_get (&self->target_ref);
     346                 :            : 
     347         [ +  + ]:         22 :   if (target == NULL)
     348                 :          1 :     goto unlock;
     349                 :            : 
     350         [ +  + ]:        189 :   for (i = 0; i < self->handlers->len; i++)
     351                 :            :     {
     352                 :        168 :       const SignalHandler *handler = g_ptr_array_index (self->handlers, i);
     353                 :            : 
     354                 :        168 :       g_assert (handler != NULL);
     355                 :        168 :       g_assert (handler->signal_id != 0);
     356                 :        168 :       g_assert (handler->closure != NULL);
     357                 :        168 :       g_assert (handler->handler_id != 0);
     358                 :            : 
     359                 :        168 :       g_signal_handler_block (target, handler->handler_id);
     360                 :            :     }
     361                 :            : 
     362                 :         21 :   g_object_unref (target);
     363                 :            : 
     364                 :         22 : unlock:
     365                 :         22 :   g_rec_mutex_unlock (&self->mutex);
     366                 :            : }
     367                 :            : 
     368                 :            : /**
     369                 :            :  * g_signal_group_unblock:
     370                 :            :  * @self: the #GSignalGroup
     371                 :            :  *
     372                 :            :  * Unblocks all signal handlers managed by @self so they will be
     373                 :            :  * called again during any signal emissions unless it is blocked
     374                 :            :  * again. Must be unblocked exactly the same number of times it
     375                 :            :  * has been blocked to become active again.
     376                 :            :  *
     377                 :            :  * Since: 2.72
     378                 :            :  */
     379                 :            : void
     380                 :         22 : g_signal_group_unblock (GSignalGroup *self)
     381                 :            : {
     382                 :            :   GObject *target;
     383                 :            :   guint i;
     384                 :            : 
     385                 :         22 :   g_return_if_fail (G_IS_SIGNAL_GROUP (self));
     386                 :         22 :   g_return_if_fail (self->block_count > 0);
     387                 :            : 
     388                 :         22 :   g_rec_mutex_lock (&self->mutex);
     389                 :            : 
     390                 :         22 :   self->block_count--;
     391                 :            : 
     392                 :         22 :   target = g_weak_ref_get (&self->target_ref);
     393         [ +  + ]:         22 :   if (target == NULL)
     394                 :          1 :     goto unlock;
     395                 :            : 
     396         [ +  + ]:        189 :   for (i = 0; i < self->handlers->len; i++)
     397                 :            :     {
     398                 :        168 :       const SignalHandler *handler = g_ptr_array_index (self->handlers, i);
     399                 :            : 
     400                 :        168 :       g_assert (handler != NULL);
     401                 :        168 :       g_assert (handler->signal_id != 0);
     402                 :        168 :       g_assert (handler->closure != NULL);
     403                 :        168 :       g_assert (handler->handler_id != 0);
     404                 :            : 
     405                 :        168 :       g_signal_handler_unblock (target, handler->handler_id);
     406                 :            :     }
     407                 :            : 
     408                 :         21 :   g_object_unref (target);
     409                 :            : 
     410                 :         22 : unlock:
     411                 :         22 :   g_rec_mutex_unlock (&self->mutex);
     412                 :            : }
     413                 :            : 
     414                 :            : /**
     415                 :            :  * g_signal_group_dup_target:
     416                 :            :  * @self: the #GSignalGroup
     417                 :            :  *
     418                 :            :  * Gets the target instance used when connecting signals.
     419                 :            :  *
     420                 :            :  * Returns: (nullable) (transfer full) (type GObject): The target instance
     421                 :            :  *
     422                 :            :  * Since: 2.72
     423                 :            :  */
     424                 :            : gpointer
     425                 :         89 : g_signal_group_dup_target (GSignalGroup *self)
     426                 :            : {
     427                 :            :   GObject *target;
     428                 :            : 
     429                 :         89 :   g_return_val_if_fail (G_IS_SIGNAL_GROUP (self), NULL);
     430                 :            : 
     431                 :         89 :   g_rec_mutex_lock (&self->mutex);
     432                 :         89 :   target = g_weak_ref_get (&self->target_ref);
     433                 :         89 :   g_rec_mutex_unlock (&self->mutex);
     434                 :            : 
     435                 :         89 :   return target;
     436                 :            : }
     437                 :            : 
     438                 :            : /**
     439                 :            :  * g_signal_group_set_target:
     440                 :            :  * @self: the #GSignalGroup.
     441                 :            :  * @target: (nullable) (type GObject) (transfer none): The target instance used
     442                 :            :  *     when connecting signals.
     443                 :            :  *
     444                 :            :  * Sets the target instance used when connecting signals. Any signal
     445                 :            :  * that has been registered with g_signal_group_connect_object() or
     446                 :            :  * similar functions will be connected to this object.
     447                 :            :  *
     448                 :            :  * If the target instance was previously set, signals will be
     449                 :            :  * disconnected from that object prior to connecting to @target.
     450                 :            :  *
     451                 :            :  * Since: 2.72
     452                 :            :  */
     453                 :            : void
     454                 :         14 : g_signal_group_set_target (GSignalGroup *self,
     455                 :            :                            gpointer      target)
     456                 :            : {
     457                 :            :   GObject *object;
     458                 :            : 
     459                 :         14 :   g_return_if_fail (G_IS_SIGNAL_GROUP (self));
     460                 :            : 
     461                 :         14 :   g_rec_mutex_lock (&self->mutex);
     462                 :            : 
     463                 :         14 :   object = g_weak_ref_get (&self->target_ref);
     464                 :            : 
     465         [ +  + ]:         14 :   if (object == (GObject *)target)
     466                 :          1 :     goto cleanup;
     467                 :            : 
     468         [ +  + ]:         13 :   if (!g_signal_group_check_target_type (self, target))
     469                 :          1 :     goto cleanup;
     470                 :            : 
     471                 :            :   /* Only emit unbind if we've ever called bind */
     472         [ +  + ]:         12 :   if (self->has_bound_at_least_once)
     473                 :          4 :     g_signal_group_unbind (self);
     474                 :            : 
     475                 :         12 :   g_signal_group_bind (self, target);
     476                 :            : 
     477                 :         12 :   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TARGET]);
     478                 :            : 
     479                 :         14 : cleanup:
     480                 :         14 :   g_clear_object (&object);
     481                 :         14 :   g_rec_mutex_unlock (&self->mutex);
     482                 :            : }
     483                 :            : 
     484                 :            : static void
     485                 :         30 : signal_handler_free (gpointer data)
     486                 :            : {
     487                 :         30 :   SignalHandler *handler = data;
     488                 :            : 
     489         [ +  - ]:         30 :   if (handler->closure != NULL)
     490                 :         30 :     g_closure_invalidate (handler->closure);
     491                 :            : 
     492                 :         30 :   handler->handler_id = 0;
     493                 :         30 :   handler->signal_id = 0;
     494                 :         30 :   handler->signal_detail = 0;
     495                 :         30 :   g_clear_pointer (&handler->closure, g_closure_unref);
     496                 :         30 :   g_slice_free (SignalHandler, handler);
     497                 :         30 : }
     498                 :            : 
     499                 :            : static void
     500                 :         14 : g_signal_group_constructed (GObject *object)
     501                 :            : {
     502                 :         14 :   GSignalGroup *self = (GSignalGroup *)object;
     503                 :            :   GObject *target;
     504                 :            : 
     505                 :         14 :   g_rec_mutex_lock (&self->mutex);
     506                 :            : 
     507                 :         14 :   target = g_weak_ref_get (&self->target_ref);
     508         [ -  + ]:         14 :   if (!g_signal_group_check_target_type (self, target))
     509                 :          0 :     g_signal_group_set_target (self, NULL);
     510                 :            : 
     511                 :         14 :   G_OBJECT_CLASS (g_signal_group_parent_class)->constructed (object);
     512                 :            : 
     513                 :         14 :   g_clear_object (&target);
     514                 :            : 
     515                 :         14 :   g_rec_mutex_unlock (&self->mutex);
     516                 :         14 : }
     517                 :            : 
     518                 :            : static void
     519                 :         14 : g_signal_group_dispose (GObject *object)
     520                 :            : {
     521                 :         14 :   GSignalGroup *self = (GSignalGroup *)object;
     522                 :            : 
     523                 :         14 :   g_rec_mutex_lock (&self->mutex);
     524                 :            : 
     525                 :         14 :   g_signal_group_gc_handlers (self);
     526                 :            : 
     527         [ +  + ]:         14 :   if (self->has_bound_at_least_once)
     528                 :          8 :     g_signal_group_unbind (self);
     529                 :            : 
     530                 :         14 :   g_clear_pointer (&self->handlers, g_ptr_array_unref);
     531                 :            : 
     532                 :         14 :   g_rec_mutex_unlock (&self->mutex);
     533                 :            : 
     534                 :         14 :   G_OBJECT_CLASS (g_signal_group_parent_class)->dispose (object);
     535                 :         14 : }
     536                 :            : 
     537                 :            : static void
     538                 :         14 : g_signal_group_finalize (GObject *object)
     539                 :            : {
     540                 :         14 :   GSignalGroup *self = (GSignalGroup *)object;
     541                 :            : 
     542                 :         14 :   g_weak_ref_clear (&self->target_ref);
     543                 :         14 :   g_rec_mutex_clear (&self->mutex);
     544                 :            : 
     545                 :         14 :   G_OBJECT_CLASS (g_signal_group_parent_class)->finalize (object);
     546                 :         14 : }
     547                 :            : 
     548                 :            : static void
     549                 :          5 : g_signal_group_get_property (GObject    *object,
     550                 :            :                              guint       prop_id,
     551                 :            :                              GValue     *value,
     552                 :            :                              GParamSpec *pspec)
     553                 :            : {
     554                 :          5 :   GSignalGroup *self = G_SIGNAL_GROUP (object);
     555                 :            : 
     556      [ +  +  - ]:          5 :   switch ((GSignalGroupProperty) prop_id)
     557                 :            :     {
     558                 :          3 :     case PROP_TARGET:
     559                 :          3 :       g_value_take_object (value, g_signal_group_dup_target (self));
     560                 :          3 :       break;
     561                 :            : 
     562                 :          2 :     case PROP_TARGET_TYPE:
     563                 :          2 :       g_value_set_gtype (value, self->target_type);
     564                 :          2 :       break;
     565                 :            : 
     566                 :          0 :     default:
     567                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     568                 :            :     }
     569                 :          5 : }
     570                 :            : 
     571                 :            : static void
     572                 :         15 : g_signal_group_set_property (GObject      *object,
     573                 :            :                              guint         prop_id,
     574                 :            :                              const GValue *value,
     575                 :            :                              GParamSpec   *pspec)
     576                 :            : {
     577                 :         15 :   GSignalGroup *self = G_SIGNAL_GROUP (object);
     578                 :            : 
     579      [ +  +  - ]:         15 :   switch ((GSignalGroupProperty) prop_id)
     580                 :            :     {
     581                 :          1 :     case PROP_TARGET:
     582                 :          1 :       g_signal_group_set_target (self, g_value_get_object (value));
     583                 :          1 :       break;
     584                 :            : 
     585                 :         14 :     case PROP_TARGET_TYPE:
     586                 :         14 :       g_signal_group_set_target_type (self, g_value_get_gtype (value));
     587                 :         14 :       break;
     588                 :            : 
     589                 :          0 :     default:
     590                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     591                 :            :     }
     592                 :         15 : }
     593                 :            : 
     594                 :            : static void
     595                 :          4 : g_signal_group_class_init (GSignalGroupClass *klass)
     596                 :            : {
     597                 :          4 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     598                 :            : 
     599                 :          4 :   object_class->constructed = g_signal_group_constructed;
     600                 :          4 :   object_class->dispose = g_signal_group_dispose;
     601                 :          4 :   object_class->finalize = g_signal_group_finalize;
     602                 :          4 :   object_class->get_property = g_signal_group_get_property;
     603                 :          4 :   object_class->set_property = g_signal_group_set_property;
     604                 :            : 
     605                 :            :   /**
     606                 :            :    * GSignalGroup:target
     607                 :            :    *
     608                 :            :    * The target instance used when connecting signals.
     609                 :            :    *
     610                 :            :    * Since: 2.72
     611                 :            :    */
     612                 :          4 :   properties[PROP_TARGET] =
     613                 :          4 :       g_param_spec_object ("target", NULL, NULL,
     614                 :            :                            G_TYPE_OBJECT,
     615                 :            :                            (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
     616                 :            : 
     617                 :            :   /**
     618                 :            :    * GSignalGroup:target-type
     619                 :            :    *
     620                 :            :    * The #GType of the target property.
     621                 :            :    *
     622                 :            :    * Since: 2.72
     623                 :            :    */
     624                 :          4 :   properties[PROP_TARGET_TYPE] =
     625                 :          4 :       g_param_spec_gtype ("target-type", NULL, NULL,
     626                 :            :                           G_TYPE_OBJECT,
     627                 :            :                           (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
     628                 :            : 
     629                 :          4 :   g_object_class_install_properties (object_class, LAST_PROP, properties);
     630                 :            : 
     631                 :            :   /**
     632                 :            :    * GSignalGroup::bind:
     633                 :            :    * @self: the #GSignalGroup
     634                 :            :    * @instance: a #GObject containing the new value for #GSignalGroup:target
     635                 :            :    *
     636                 :            :    * This signal is emitted when #GSignalGroup:target is set to a new value
     637                 :            :    * other than %NULL. It is similar to #GObject::notify on `target` except it
     638                 :            :    * will not emit when #GSignalGroup:target is %NULL and also allows for
     639                 :            :    * receiving the #GObject without a data-race.
     640                 :            :    *
     641                 :            :    * Since: 2.72
     642                 :            :    */
     643                 :          4 :   signals[BIND] =
     644                 :          4 :       g_signal_new ("bind",
     645                 :            :                     G_TYPE_FROM_CLASS (klass),
     646                 :            :                     G_SIGNAL_RUN_LAST,
     647                 :            :                     0,
     648                 :            :                     NULL, NULL, NULL,
     649                 :            :                     G_TYPE_NONE,
     650                 :            :                     1,
     651                 :            :                     G_TYPE_OBJECT);
     652                 :            : 
     653                 :            :   /**
     654                 :            :    * GSignalGroup::unbind:
     655                 :            :    * @self: a #GSignalGroup
     656                 :            :    *
     657                 :            :    * This signal is emitted when the target instance of @self is set to a
     658                 :            :    * new #GObject.
     659                 :            :    *
     660                 :            :    * This signal will only be emitted if the previous target of @self is
     661                 :            :    * non-%NULL.
     662                 :            :    *
     663                 :            :    * Since: 2.72
     664                 :            :    */
     665                 :          4 :   signals[UNBIND] =
     666                 :          4 :       g_signal_new ("unbind",
     667                 :            :                     G_TYPE_FROM_CLASS (klass),
     668                 :            :                     G_SIGNAL_RUN_LAST,
     669                 :            :                     0,
     670                 :            :                     NULL, NULL, NULL,
     671                 :            :                     G_TYPE_NONE,
     672                 :            :                     0);
     673                 :          4 : }
     674                 :            : 
     675                 :            : static void
     676                 :         14 : g_signal_group_init (GSignalGroup *self)
     677                 :            : {
     678                 :         14 :   g_rec_mutex_init (&self->mutex);
     679                 :         14 :   self->handlers = g_ptr_array_new_with_free_func (signal_handler_free);
     680                 :         14 :   self->target_type = G_TYPE_OBJECT;
     681                 :         14 : }
     682                 :            : 
     683                 :            : /**
     684                 :            :  * g_signal_group_new:
     685                 :            :  * @target_type: the #GType of the target instance.
     686                 :            :  *
     687                 :            :  * Creates a new #GSignalGroup for target instances of @target_type.
     688                 :            :  *
     689                 :            :  * Returns: (transfer full): a new #GSignalGroup
     690                 :            :  *
     691                 :            :  * Since: 2.72
     692                 :            :  */
     693                 :            : GSignalGroup *
     694                 :         14 : g_signal_group_new (GType target_type)
     695                 :            : {
     696                 :         14 :   g_return_val_if_fail (g_type_is_a (target_type, G_TYPE_OBJECT), NULL);
     697                 :            : 
     698                 :         13 :   return g_object_new (G_TYPE_SIGNAL_GROUP,
     699                 :            :                        "target-type", target_type,
     700                 :            :                        NULL);
     701                 :            : }
     702                 :            : 
     703                 :            : static gboolean
     704                 :         32 : g_signal_group_connect_closure_ (GSignalGroup   *self,
     705                 :            :                                  const gchar    *detailed_signal,
     706                 :            :                                  GClosure       *closure,
     707                 :            :                                  gboolean        after)
     708                 :            : {
     709                 :            :   GObject *target;
     710                 :            :   SignalHandler *handler;
     711                 :            :   guint signal_id;
     712                 :            :   GQuark signal_detail;
     713                 :            : 
     714                 :         32 :   g_return_val_if_fail (G_IS_SIGNAL_GROUP (self), FALSE);
     715                 :         32 :   g_return_val_if_fail (detailed_signal != NULL, FALSE);
     716                 :         32 :   g_return_val_if_fail (closure != NULL, FALSE);
     717                 :            : 
     718         [ +  + ]:         32 :   if (!g_signal_parse_name (detailed_signal, self->target_type,
     719                 :            :                             &signal_id, &signal_detail, TRUE))
     720                 :            :     {
     721                 :          1 :       g_critical ("Invalid signal name ā€œ%sā€", detailed_signal);
     722                 :          1 :       return FALSE;
     723                 :            :     }
     724                 :            : 
     725                 :         31 :   g_rec_mutex_lock (&self->mutex);
     726                 :            : 
     727         [ +  + ]:         31 :   if (self->has_bound_at_least_once)
     728                 :            :     {
     729                 :          1 :       g_critical ("Cannot add signals after setting target");
     730                 :          1 :       g_rec_mutex_unlock (&self->mutex);
     731                 :          1 :       return FALSE;
     732                 :            :     }
     733                 :            : 
     734                 :         30 :   handler = g_slice_new0 (SignalHandler);
     735                 :         30 :   handler->group = self;
     736                 :         30 :   handler->signal_id = signal_id;
     737                 :         30 :   handler->signal_detail = signal_detail;
     738                 :         30 :   handler->closure = g_closure_ref (closure);
     739                 :         30 :   handler->connect_after = after;
     740                 :            : 
     741                 :         30 :   g_closure_sink (closure);
     742                 :            : 
     743                 :         30 :   g_ptr_array_add (self->handlers, handler);
     744                 :            : 
     745                 :         30 :   target = g_weak_ref_get (&self->target_ref);
     746                 :            : 
     747         [ -  + ]:         30 :   if (target != NULL)
     748                 :            :     {
     749                 :          0 :       g_signal_group_bind_handler (self, handler, target);
     750                 :          0 :       g_object_unref (target);
     751                 :            :     }
     752                 :            : 
     753                 :            :   /* Lazily remove any old handlers on connect */
     754                 :         30 :   g_signal_group_gc_handlers (self);
     755                 :            : 
     756                 :         30 :   g_rec_mutex_unlock (&self->mutex);
     757                 :         30 :   return TRUE;
     758                 :            : }
     759                 :            : 
     760                 :            : /**
     761                 :            :  * g_signal_group_connect_closure:
     762                 :            :  * @self: a #GSignalGroup
     763                 :            :  * @detailed_signal: a string of the form `signal-name` with optional `::signal-detail`
     764                 :            :  * @closure: (not nullable): the closure to connect.
     765                 :            :  * @after: whether the handler should be called before or after the
     766                 :            :  *  default handler of the signal.
     767                 :            :  *
     768                 :            :  * Connects @closure to the signal @detailed_signal on #GSignalGroup:target.
     769                 :            :  *
     770                 :            :  * You cannot connect a signal handler after #GSignalGroup:target has been set.
     771                 :            :  *
     772                 :            :  * Since: 2.74
     773                 :            :  */
     774                 :            : void
     775                 :          6 : g_signal_group_connect_closure (GSignalGroup   *self,
     776                 :            :                                 const gchar    *detailed_signal,
     777                 :            :                                 GClosure       *closure,
     778                 :            :                                 gboolean        after)
     779                 :            : {
     780                 :          6 :   g_signal_group_connect_closure_ (self, detailed_signal, closure, after);
     781                 :          6 : }
     782                 :            : 
     783                 :            : static void
     784                 :         27 : g_signal_group_connect_full (GSignalGroup   *self,
     785                 :            :                              const gchar    *detailed_signal,
     786                 :            :                              GCallback       c_handler,
     787                 :            :                              gpointer        data,
     788                 :            :                              GClosureNotify  notify,
     789                 :            :                              GConnectFlags   flags,
     790                 :            :                              gboolean        is_object)
     791                 :            : {
     792                 :            :   GClosure *closure;
     793                 :            : 
     794                 :         27 :   g_return_if_fail (c_handler != NULL);
     795                 :         26 :   g_return_if_fail (!is_object || G_IS_OBJECT (data));
     796                 :            : 
     797         [ +  + ]:         26 :   if ((flags & G_CONNECT_SWAPPED) != 0)
     798                 :          3 :     closure = g_cclosure_new_swap (c_handler, data, notify);
     799                 :            :   else
     800                 :         23 :     closure = g_cclosure_new (c_handler, data, notify);
     801                 :            : 
     802         [ +  + ]:         26 :   if (is_object)
     803                 :            :     {
     804                 :            :       /* Set closure->is_invalid when data is disposed. We only track this to avoid
     805                 :            :        * reconnecting in the future. However, we do a round of cleanup when ever we
     806                 :            :        * connect a new object or the target changes to GC the old handlers.
     807                 :            :        */
     808                 :          4 :       g_object_watch_closure (data, closure);
     809                 :            :     }
     810                 :            : 
     811         [ +  + ]:         26 :   if (!g_signal_group_connect_closure_ (self,
     812                 :            :                                         detailed_signal,
     813                 :            :                                         closure,
     814                 :         26 :                                         (flags & G_CONNECT_AFTER) != 0))
     815                 :          2 :     g_closure_unref (closure);
     816                 :            : }
     817                 :            : 
     818                 :            : /**
     819                 :            :  * g_signal_group_connect_object: (skip)
     820                 :            :  * @self: a #GSignalGroup
     821                 :            :  * @detailed_signal: a string of the form `signal-name` with optional `::signal-detail`
     822                 :            :  * @c_handler: (scope notified): the #GCallback to connect
     823                 :            :  * @object: (not nullable) (transfer none): the #GObject to pass as data to @c_handler calls
     824                 :            :  * @flags: #GConnectFlags for the signal connection
     825                 :            :  *
     826                 :            :  * Connects @c_handler to the signal @detailed_signal on #GSignalGroup:target.
     827                 :            :  *
     828                 :            :  * Ensures that the @object stays alive during the call to @c_handler
     829                 :            :  * by temporarily adding a reference count. When the @object is destroyed
     830                 :            :  * the signal handler will automatically be removed.
     831                 :            :  *
     832                 :            :  * You cannot connect a signal handler after #GSignalGroup:target has been set.
     833                 :            :  *
     834                 :            :  * Since: 2.72
     835                 :            :  */
     836                 :            : void
     837                 :          4 : g_signal_group_connect_object (GSignalGroup  *self,
     838                 :            :                                const gchar   *detailed_signal,
     839                 :            :                                GCallback      c_handler,
     840                 :            :                                gpointer       object,
     841                 :            :                                GConnectFlags  flags)
     842                 :            : {
     843                 :          4 :   g_return_if_fail (G_IS_OBJECT (object));
     844                 :            : 
     845                 :          4 :   g_signal_group_connect_full (self, detailed_signal, c_handler, object, NULL,
     846                 :            :                                flags, TRUE);
     847                 :            : }
     848                 :            : 
     849                 :            : /**
     850                 :            :  * g_signal_group_connect_data:
     851                 :            :  * @self: a #GSignalGroup
     852                 :            :  * @detailed_signal: a string of the form "signal-name::detail"
     853                 :            :  * @c_handler: (scope notified) (closure data) (destroy notify): the #GCallback to connect
     854                 :            :  * @data: the data to pass to @c_handler calls
     855                 :            :  * @notify: function to be called when disposing of @self
     856                 :            :  * @flags: the flags used to create the signal connection
     857                 :            :  *
     858                 :            :  * Connects @c_handler to the signal @detailed_signal
     859                 :            :  * on the target instance of @self.
     860                 :            :  *
     861                 :            :  * You cannot connect a signal handler after #GSignalGroup:target has been set.
     862                 :            :  *
     863                 :            :  * Since: 2.72
     864                 :            :  */
     865                 :            : void
     866                 :          3 : g_signal_group_connect_data (GSignalGroup   *self,
     867                 :            :                              const gchar    *detailed_signal,
     868                 :            :                              GCallback       c_handler,
     869                 :            :                              gpointer        data,
     870                 :            :                              GClosureNotify  notify,
     871                 :            :                              GConnectFlags   flags)
     872                 :            : {
     873                 :          3 :   g_signal_group_connect_full (self, detailed_signal, c_handler, data, notify,
     874                 :            :                                flags, FALSE);
     875                 :          3 : }
     876                 :            : 
     877                 :            : /**
     878                 :            :  * g_signal_group_connect: (skip)
     879                 :            :  * @self: a #GSignalGroup
     880                 :            :  * @detailed_signal: a string of the form "signal-name::detail"
     881                 :            :  * @c_handler: (scope notified): the #GCallback to connect
     882                 :            :  * @data: the data to pass to @c_handler calls
     883                 :            :  *
     884                 :            :  * Connects @c_handler to the signal @detailed_signal
     885                 :            :  * on the target instance of @self.
     886                 :            :  *
     887                 :            :  * You cannot connect a signal handler after #GSignalGroup:target has been set.
     888                 :            :  *
     889                 :            :  * Since: 2.72
     890                 :            :  */
     891                 :            : void
     892                 :         14 : g_signal_group_connect (GSignalGroup *self,
     893                 :            :                         const gchar  *detailed_signal,
     894                 :            :                         GCallback     c_handler,
     895                 :            :                         gpointer      data)
     896                 :            : {
     897                 :         14 :   g_signal_group_connect_full (self, detailed_signal, c_handler, data, NULL,
     898                 :            :                                0, FALSE);
     899                 :         14 : }
     900                 :            : 
     901                 :            : /**
     902                 :            :  * g_signal_group_connect_after: (skip)
     903                 :            :  * @self: a #GSignalGroup
     904                 :            :  * @detailed_signal: a string of the form "signal-name::detail"
     905                 :            :  * @c_handler: (scope notified): the #GCallback to connect
     906                 :            :  * @data: the data to pass to @c_handler calls
     907                 :            :  *
     908                 :            :  * Connects @c_handler to the signal @detailed_signal
     909                 :            :  * on the target instance of @self.
     910                 :            :  *
     911                 :            :  * The @c_handler will be called after the default handler of the signal.
     912                 :            :  *
     913                 :            :  * You cannot connect a signal handler after #GSignalGroup:target has been set.
     914                 :            :  *
     915                 :            :  * Since: 2.72
     916                 :            :  */
     917                 :            : void
     918                 :          3 : g_signal_group_connect_after (GSignalGroup *self,
     919                 :            :                               const gchar  *detailed_signal,
     920                 :            :                               GCallback     c_handler,
     921                 :            :                               gpointer      data)
     922                 :            : {
     923                 :          3 :   g_signal_group_connect_full (self, detailed_signal, c_handler,
     924                 :            :                                data, NULL, G_CONNECT_AFTER, FALSE);
     925                 :          3 : }
     926                 :            : 
     927                 :            : /**
     928                 :            :  * g_signal_group_connect_swapped:
     929                 :            :  * @self: a #GSignalGroup
     930                 :            :  * @detailed_signal: a string of the form "signal-name::detail"
     931                 :            :  * @c_handler: (scope async): the #GCallback to connect
     932                 :            :  * @data: the data to pass to @c_handler calls
     933                 :            :  *
     934                 :            :  * Connects @c_handler to the signal @detailed_signal
     935                 :            :  * on the target instance of @self.
     936                 :            :  *
     937                 :            :  * The instance on which the signal is emitted and @data
     938                 :            :  * will be swapped when calling @c_handler.
     939                 :            :  *
     940                 :            :  * You cannot connect a signal handler after #GSignalGroup:target has been set.
     941                 :            :  *
     942                 :            :  * Since: 2.72
     943                 :            :  */
     944                 :            : void
     945                 :          3 : g_signal_group_connect_swapped (GSignalGroup *self,
     946                 :            :                                 const gchar  *detailed_signal,
     947                 :            :                                 GCallback     c_handler,
     948                 :            :                                 gpointer      data)
     949                 :            : {
     950                 :          3 :   g_signal_group_connect_full (self, detailed_signal, c_handler, data, NULL,
     951                 :            :                                G_CONNECT_SWAPPED, FALSE);
     952                 :          3 : }

Generated by: LCOV version 1.14