LCOV - code coverage report
Current view: top level - gobject - gsignalgroup.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 97.5 % 284 277
Test Date: 2024-11-26 05:23:01 Functions: 100.0 % 31 31
Branches: - 0 0

             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 2.0-1