gsignal.c 115 KB
Newer Older
1
/* GObject - GLib Type, Object, Parameter and Signal Library
2
 * Copyright (C) 2000-2001 Red Hat, Inc.
3 4 5 6 7 8 9 10 11 12 13 14
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
15
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 17 18 19
 *
 * this code is based on the original GtkSignal implementation
 * for the Gtk+ library by Peter Mattis <petm@xcf.berkeley.edu>
 */
20 21 22 23 24 25 26 27 28 29 30

/*
 * MT safe
 */

#include "config.h"

#include <string.h>
#include <signal.h>

#include "gsignal.h"
31
#include "gtype-private.h"
32 33 34 35 36
#include "gbsearcharray.h"
#include "gvaluecollector.h"
#include "gvaluetypes.h"
#include "gobject.h"
#include "genums.h"
37
#include "gobject_trace.h"
38 39


Stefan Kost's avatar
Stefan Kost committed
40 41
/**
 * SECTION:signals
Matthias Clasen's avatar
Matthias Clasen committed
42 43 44
 * @short_description: A means for customization of object behaviour
 *     and a general purpose notification mechanism
 * @title: Signals
45
 *
46 47 48 49 50 51 52 53 54
 * The basic concept of the signal system is that of the emission
 * of a signal. Signals are introduced per-type and are identified
 * through strings. Signals introduced for a parent type are available
 * in derived types as well, so basically they are a per-type facility
 * that is inherited.
 *
 * A signal emission mainly involves invocation of a certain set of
 * callbacks in precisely defined manner. There are two main categories
 * of such callbacks, per-object ones and user provided ones.
Matthias Clasen's avatar
Matthias Clasen committed
55 56 57
 * (Although signals can deal with any kind of instantiatable type, I'm
 * referring to those types as "object types" in the following, simply
 * because that is the context most users will encounter signals in.)
Stefan Kost's avatar
Stefan Kost committed
58 59 60
 * The per-object callbacks are most often referred to as "object method
 * handler" or "default (signal) handler", while user provided callbacks are
 * usually just called "signal handler".
61
 *
Stefan Kost's avatar
Stefan Kost committed
62 63
 * The object method handler is provided at signal creation time (this most
 * frequently happens at the end of an object class' creation), while user
64 65
 * provided handlers are frequently connected and disconnected to/from a
 * certain signal on certain object instances.
66
 *
Stefan Kost's avatar
Stefan Kost committed
67
 * A signal emission consists of five stages, unless prematurely stopped:
68 69 70 71 72 73 74 75 76 77 78 79
 *
 * 1. Invocation of the object method handler for %G_SIGNAL_RUN_FIRST signals
 *
 * 2. Invocation of normal user-provided signal handlers (where the @after
 *    flag is not set)
 *
 * 3. Invocation of the object method handler for %G_SIGNAL_RUN_LAST signals
 *
 * 4. Invocation of user provided signal handlers (where the @after flag is set)
 *
 * 5. Invocation of the object method handler for %G_SIGNAL_RUN_CLEANUP signals
 
Stefan Kost's avatar
Stefan Kost committed
80 81
 * The user-provided signal handlers are called in the order they were
 * connected in.
82
 *
Stefan Kost's avatar
Stefan Kost committed
83 84 85
 * All handlers may prematurely stop a signal emission, and any number of
 * handlers may be connected, disconnected, blocked or unblocked during
 * a signal emission.
86
 *
Stefan Kost's avatar
Stefan Kost committed
87 88
 * There are certain criteria for skipping user handlers in stages 2 and 4
 * of a signal emission.
89 90 91 92 93
 *
 * First, user handlers may be blocked. Blocked handlers are omitted during
 * callback invocation, to return from the blocked state, a handler has to
 * get unblocked exactly the same amount of times it has been blocked before.
 *
Stefan Kost's avatar
Stefan Kost committed
94
 * Second, upon emission of a %G_SIGNAL_DETAILED signal, an additional
95
 * @detail argument passed in to g_signal_emit() has to match the detail
Stefan Kost's avatar
Stefan Kost committed
96 97 98 99 100
 * argument of the signal handler currently subject to invocation.
 * Specification of no detail argument for signal handlers (omission of the
 * detail part of the signal specification upon connection) serves as a
 * wildcard and matches any detail argument passed in to emission.
 */
101 102


Matthias Clasen's avatar
Matthias Clasen committed
103
#define REPORT_BUG      "please report occurrence circumstances to gtk-devel-list@gnome.org"
104 105

/* --- typedefs --- */
106 107 108 109 110 111
typedef struct _SignalNode   SignalNode;
typedef struct _SignalKey    SignalKey;
typedef struct _Emission     Emission;
typedef struct _Handler      Handler;
typedef struct _HandlerList  HandlerList;
typedef struct _HandlerMatch HandlerMatch;
112 113 114 115 116 117 118 119 120 121
typedef enum
{
  EMISSION_STOP,
  EMISSION_RUN,
  EMISSION_HOOK,
  EMISSION_RESTART
} EmissionState;


/* --- prototypes --- */
122 123 124 125 126 127 128 129 130 131 132 133
static inline guint		signal_id_lookup	(GQuark		  quark,
							 GType		  itype);
static	      void		signal_destroy_R	(SignalNode	 *signal_node);
static inline HandlerList*	handler_list_ensure	(guint		  signal_id,
							 gpointer	  instance);
static inline HandlerList*	handler_list_lookup	(guint		  signal_id,
							 gpointer	  instance);
static inline Handler*		handler_new		(gboolean	  after);
static	      void		handler_insert		(guint		  signal_id,
							 gpointer	  instance,
							 Handler	 *handler);
static	      Handler*		handler_lookup		(gpointer	  instance,
Tim Janik's avatar
Tim Janik committed
134
							 gulong		  handler_id,
135
							 GClosure        *closure,
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
							 guint		 *signal_id_p);
static inline HandlerMatch*	handler_match_prepend	(HandlerMatch	 *list,
							 Handler	 *handler,
							 guint		  signal_id);
static inline HandlerMatch*	handler_match_free1_R	(HandlerMatch	 *node,
							 gpointer	  instance);
static	      HandlerMatch*	handlers_find		(gpointer	  instance,
							 GSignalMatchType mask,
							 guint		  signal_id,
							 GQuark		  detail,
							 GClosure	 *closure,
							 gpointer	  func,
							 gpointer	  data,
							 gboolean	  one_and_only);
static inline void		handler_ref		(Handler	 *handler);
static inline void		handler_unref_R		(guint		  signal_id,
							 gpointer	  instance,
							 Handler	 *handler);
Tim Janik's avatar
Tim Janik committed
154 155
static gint			handler_lists_cmp	(gconstpointer	  node1,
							 gconstpointer	  node2);
156 157 158
static inline void		emission_push		(Emission	 *emission);
static inline void		emission_pop		(Emission	 *emission);
static inline Emission*		emission_find		(guint		  signal_id,
159 160
							 GQuark		  detail,
							 gpointer	  instance);
Tim Janik's avatar
Tim Janik committed
161 162
static gint			class_closures_cmp	(gconstpointer	  node1,
							 gconstpointer	  node2);
163 164
static gint			signal_key_cmp		(gconstpointer	  node1,
							 gconstpointer	  node2);
165
static	      gboolean		signal_emit_unlocked_R	(SignalNode	 *node,
166 167 168 169
							 GQuark		  detail,
							 gpointer	  instance,
							 GValue		 *return_value,
							 const GValue	 *instance_and_params);
170 171 172 173 174 175
static       void               add_invalid_closure_notify    (Handler         *handler,
							       gpointer         instance);
static       void               remove_invalid_closure_notify (Handler         *handler,
							       gpointer         instance);
static       void               invalid_closure_notify  (gpointer         data,
							 GClosure        *closure);
176
static const gchar *            type_debug_name         (GType            type);
Marc-André Lureau's avatar
Marc-André Lureau committed
177
static void                     node_check_deprecated   (const SignalNode *node);
178
static void                     node_update_single_va_closure (SignalNode *node);
179 180 181


/* --- structures --- */
182 183 184 185 186
typedef struct
{
  GSignalAccumulator func;
  gpointer           data;
} SignalAccumulator;
187 188 189 190 191 192 193
typedef struct
{
  GHook hook;
  GQuark detail;
} SignalHook;
#define	SIGNAL_HOOK(hook)	((SignalHook*) (hook))

194 195 196 197 198
struct _SignalNode
{
  /* permanent portion */
  guint              signal_id;
  GType              itype;
199
  const gchar       *name;
200 201 202
  guint              destroyed : 1;
  
  /* reinitializable portion */
Marc-André Lureau's avatar
Marc-André Lureau committed
203
  guint              flags : 9;
204
  guint              n_params : 8;
205 206
  guint              single_va_closure_is_valid : 1;
  guint              single_va_closure_is_after : 1;
207 208
  GType		    *param_types; /* mangled with G_SIGNAL_TYPE_STATIC_SCOPE flag */
  GType		     return_type; /* mangled with G_SIGNAL_TYPE_STATIC_SCOPE flag */
Tim Janik's avatar
Tim Janik committed
209
  GBSearchArray     *class_closure_bsa;
210
  SignalAccumulator *accumulator;
211
  GSignalCMarshaller c_marshaller;
212
  GSignalCVaMarshaller va_marshaller;
213
  GHookList         *emission_hooks;
214 215

  GClosure *single_va_closure;
216
};
217 218

#define	SINGLE_VA_CLOSURE_EMPTY_MAGIC GINT_TO_POINTER(1)	/* indicates single_va_closure is valid but empty */
219 220 221 222 223 224 225 226 227 228

struct _SignalKey
{
  GType  itype;
  GQuark quark;
  guint  signal_id;
};

struct _Emission
{
Tim Janik's avatar
Tim Janik committed
229 230 231 232 233
  Emission             *next;
  gpointer              instance;
  GSignalInvocationHint ihint;
  EmissionState         state;
  GType			chain_type;
234 235 236 237 238 239
};

struct _HandlerList
{
  guint    signal_id;
  Handler *handlers;
240 241
  Handler *tail_before;  /* normal signal handlers are appended here  */
  Handler *tail_after;   /* CONNECT_AFTER handlers are appended here  */
242
};
243

244 245
struct _Handler
{
Tim Janik's avatar
Tim Janik committed
246
  gulong        sequential_number;
247 248
  Handler      *next;
  Handler      *prev;
249
  GQuark	detail;
250 251 252
  guint         ref_count;
  guint         block_count : 16;
#define HANDLER_MAX_BLOCK_COUNT (1 << 16)
253
  guint         after : 1;
254
  guint         has_invalid_closure_notify : 1;
255 256
  GClosure     *closure;
};
257 258 259 260
struct _HandlerMatch
{
  Handler      *handler;
  HandlerMatch *next;
261
  guint         signal_id;
262
};
263

Tim Janik's avatar
Tim Janik committed
264 265 266 267 268 269
typedef struct
{
  GType     instance_type; /* 0 for default closure */
  GClosure *closure;
} ClassClosure;

270 271

/* --- variables --- */
Tim Janik's avatar
Tim Janik committed
272
static GBSearchArray *g_signal_key_bsa = NULL;
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
static const GBSearchConfig g_signal_key_bconfig = {
  sizeof (SignalKey),
  signal_key_cmp,
  G_BSEARCH_ARRAY_ALIGN_POWER2,
};
static GBSearchConfig g_signal_hlbsa_bconfig = {
  sizeof (HandlerList),
  handler_lists_cmp,
  0,
};
static GBSearchConfig g_class_closure_bconfig = {
  sizeof (ClassClosure),
  class_closures_cmp,
  0,
};
288
static GHashTable    *g_handler_list_bsa_ht = NULL;
289
static Emission      *g_emissions = NULL;
Tim Janik's avatar
Tim Janik committed
290
static gulong         g_handler_sequential_number = 1;
291
G_LOCK_DEFINE_STATIC (g_signal_mutex);
292 293
#define	SIGNAL_LOCK()		G_LOCK (g_signal_mutex)
#define	SIGNAL_UNLOCK()		G_UNLOCK (g_signal_mutex)
294 295 296 297 298 299 300


/* --- signal nodes --- */
static guint          g_n_signal_nodes = 0;
static SignalNode   **g_signal_nodes = NULL;

static inline SignalNode*
301
LOOKUP_SIGNAL_NODE (guint signal_id)
302 303 304 305 306 307 308 309 310 311 312 313 314
{
  if (signal_id < g_n_signal_nodes)
    return g_signal_nodes[signal_id];
  else
    return NULL;
}


/* --- functions --- */
static inline guint
signal_id_lookup (GQuark quark,
		  GType  itype)
{
315 316 317 318 319 320
  GType *ifaces, type = itype;
  SignalKey key;
  guint n_ifaces;

  key.quark = quark;

Matthias Clasen's avatar
Matthias Clasen committed
321
  /* try looking up signals for this type and its ancestors */
322 323
  do
    {
324
      SignalKey *signal_key;
325
      
326
      key.itype = type;
Tim Janik's avatar
Tim Janik committed
327
      signal_key = g_bsearch_array_lookup (g_signal_key_bsa, &g_signal_key_bconfig, &key);
328 329 330 331
      
      if (signal_key)
	return signal_key->signal_id;
      
332
      type = g_type_parent (type);
333
    }
334 335 336 337 338 339 340 341 342
  while (type);

  /* no luck, try interfaces it exports */
  ifaces = g_type_interfaces (itype, &n_ifaces);
  while (n_ifaces--)
    {
      SignalKey *signal_key;

      key.itype = ifaces[n_ifaces];
Tim Janik's avatar
Tim Janik committed
343
      signal_key = g_bsearch_array_lookup (g_signal_key_bsa, &g_signal_key_bconfig, &key);
344 345 346 347 348 349 350 351

      if (signal_key)
	{
	  g_free (ifaces);
	  return signal_key->signal_id;
	}
    }
  g_free (ifaces);
352
  
353
  return 0;
354 355
}

Tim Janik's avatar
Tim Janik committed
356 357 358 359 360 361 362 363 364
static gint
class_closures_cmp (gconstpointer node1,
		    gconstpointer node2)
{
  const ClassClosure *c1 = node1, *c2 = node2;
  
  return G_BSEARCH_ARRAY_CMP (c1->instance_type, c2->instance_type);
}

365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
static gint
handler_lists_cmp (gconstpointer node1,
                   gconstpointer node2)
{
  const HandlerList *hlist1 = node1, *hlist2 = node2;
  
  return G_BSEARCH_ARRAY_CMP (hlist1->signal_id, hlist2->signal_id);
}

static inline HandlerList*
handler_list_ensure (guint    signal_id,
		     gpointer instance)
{
  GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
  HandlerList key;
  
Tim Janik's avatar
Tim Janik committed
381
  key.signal_id = signal_id;
382 383 384
  key.handlers    = NULL;
  key.tail_before = NULL;
  key.tail_after  = NULL;
385 386
  if (!hlbsa)
    {
387 388
      hlbsa = g_bsearch_array_create (&g_signal_hlbsa_bconfig);
      hlbsa = g_bsearch_array_insert (hlbsa, &g_signal_hlbsa_bconfig, &key);
389 390
      g_hash_table_insert (g_handler_list_bsa_ht, instance, hlbsa);
    }
Tim Janik's avatar
Tim Janik committed
391 392 393 394
  else
    {
      GBSearchArray *o = hlbsa;

395
      hlbsa = g_bsearch_array_insert (o, &g_signal_hlbsa_bconfig, &key);
Tim Janik's avatar
Tim Janik committed
396 397 398 399
      if (hlbsa != o)
	g_hash_table_insert (g_handler_list_bsa_ht, instance, hlbsa);
    }
  return g_bsearch_array_lookup (hlbsa, &g_signal_hlbsa_bconfig, &key);
400 401 402 403 404 405 406 407 408 409 410
}

static inline HandlerList*
handler_list_lookup (guint    signal_id,
		     gpointer instance)
{
  GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
  HandlerList key;
  
  key.signal_id = signal_id;
  
Tim Janik's avatar
Tim Janik committed
411
  return hlbsa ? g_bsearch_array_lookup (hlbsa, &g_signal_hlbsa_bconfig, &key) : NULL;
412 413 414
}

static Handler*
415 416 417 418
handler_lookup (gpointer  instance,
		gulong    handler_id,
		GClosure *closure,
		guint    *signal_id_p)
419 420 421 422 423 424 425 426 427
{
  GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
  
  if (hlbsa)
    {
      guint i;
      
      for (i = 0; i < hlbsa->n_nodes; i++)
        {
Tim Janik's avatar
Tim Janik committed
428
          HandlerList *hlist = g_bsearch_array_get_nth (hlbsa, &g_signal_hlbsa_bconfig, i);
429 430 431
          Handler *handler;
          
          for (handler = hlist->handlers; handler; handler = handler->next)
432
            if (closure ? (handler->closure == closure) : (handler->sequential_number == handler_id))
433 434 435 436 437 438 439 440 441 442 443 444
              {
                if (signal_id_p)
                  *signal_id_p = hlist->signal_id;
		
                return handler;
              }
        }
    }
  
  return NULL;
}

445 446 447 448 449 450 451
static inline HandlerMatch*
handler_match_prepend (HandlerMatch *list,
		       Handler      *handler,
		       guint	     signal_id)
{
  HandlerMatch *node;
  
452
  node = g_slice_new (HandlerMatch);
453 454
  node->handler = handler;
  node->next = list;
455
  node->signal_id = signal_id;
456 457 458 459 460 461 462 463 464 465
  handler_ref (handler);
  
  return node;
}
static inline HandlerMatch*
handler_match_free1_R (HandlerMatch *node,
		       gpointer      instance)
{
  HandlerMatch *next = node->next;
  
466 467
  handler_unref_R (node->signal_id, instance, node->handler);
  g_slice_free (HandlerMatch, node);
468 469 470 471 472 473 474 475 476 477 478 479 480
  
  return next;
}

static HandlerMatch*
handlers_find (gpointer         instance,
	       GSignalMatchType mask,
	       guint            signal_id,
	       GQuark           detail,
	       GClosure        *closure,
	       gpointer         func,
	       gpointer         data,
	       gboolean         one_and_only)
481
{
482 483
  HandlerMatch *mlist = NULL;
  
484 485 486 487
  if (mask & G_SIGNAL_MATCH_ID)
    {
      HandlerList *hlist = handler_list_lookup (signal_id, instance);
      Handler *handler;
Elliot Lee's avatar
Elliot Lee committed
488
      SignalNode *node = NULL;
489
      
490
      if (mask & G_SIGNAL_MATCH_FUNC)
491 492 493 494 495
	{
	  node = LOOKUP_SIGNAL_NODE (signal_id);
	  if (!node || !node->c_marshaller)
	    return NULL;
	}
496
      
497
      mask = ~mask;
498 499 500 501 502 503 504
      for (handler = hlist ? hlist->handlers : NULL; handler; handler = handler->next)
        if (handler->sequential_number &&
	    ((mask & G_SIGNAL_MATCH_DETAIL) || handler->detail == detail) &&
	    ((mask & G_SIGNAL_MATCH_CLOSURE) || handler->closure == closure) &&
            ((mask & G_SIGNAL_MATCH_DATA) || handler->closure->data == data) &&
	    ((mask & G_SIGNAL_MATCH_UNBLOCKED) || handler->block_count == 0) &&
	    ((mask & G_SIGNAL_MATCH_FUNC) || (handler->closure->marshal == node->c_marshaller &&
505
					      G_REAL_CLOSURE (handler->closure)->meta_marshal == NULL &&
506 507 508 509 510 511
					      ((GCClosure*) handler->closure)->callback == func)))
	  {
	    mlist = handler_match_prepend (mlist, handler, signal_id);
	    if (one_and_only)
	      return mlist;
	  }
512 513 514 515 516 517 518 519 520 521 522 523
    }
  else
    {
      GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
      
      mask = ~mask;
      if (hlbsa)
        {
          guint i;
          
          for (i = 0; i < hlbsa->n_nodes; i++)
            {
Tim Janik's avatar
Tim Janik committed
524
              HandlerList *hlist = g_bsearch_array_get_nth (hlbsa, &g_signal_hlbsa_bconfig, i);
Elliot Lee's avatar
Elliot Lee committed
525
	      SignalNode *node = NULL;
526 527 528 529 530 531 532 533
              Handler *handler;
              
	      if (!(mask & G_SIGNAL_MATCH_FUNC))
		{
		  node = LOOKUP_SIGNAL_NODE (hlist->signal_id);
		  if (!node->c_marshaller)
		    continue;
		}
534
	      
535
              for (handler = hlist->handlers; handler; handler = handler->next)
536 537 538 539 540 541
		if (handler->sequential_number &&
		    ((mask & G_SIGNAL_MATCH_DETAIL) || handler->detail == detail) &&
                    ((mask & G_SIGNAL_MATCH_CLOSURE) || handler->closure == closure) &&
                    ((mask & G_SIGNAL_MATCH_DATA) || handler->closure->data == data) &&
		    ((mask & G_SIGNAL_MATCH_UNBLOCKED) || handler->block_count == 0) &&
		    ((mask & G_SIGNAL_MATCH_FUNC) || (handler->closure->marshal == node->c_marshaller &&
542
						      G_REAL_CLOSURE (handler->closure)->meta_marshal == NULL &&
543 544 545 546 547 548
						      ((GCClosure*) handler->closure)->callback == func)))
		  {
		    mlist = handler_match_prepend (mlist, handler, hlist->signal_id);
		    if (one_and_only)
		      return mlist;
		  }
549 550 551 552
            }
        }
    }
  
553
  return mlist;
554 555 556 557 558
}

static inline Handler*
handler_new (gboolean after)
{
559
  Handler *handler = g_slice_new (Handler);
560
#ifndef G_DISABLE_CHECKS
Tim Janik's avatar
Tim Janik committed
561
  if (g_handler_sequential_number < 1)
562 563 564
    g_error (G_STRLOC ": handler id overflow, %s", REPORT_BUG);
#endif
  
Tim Janik's avatar
Tim Janik committed
565
  handler->sequential_number = g_handler_sequential_number++;
566 567
  handler->prev = NULL;
  handler->next = NULL;
568
  handler->detail = 0;
569 570 571 572
  handler->ref_count = 1;
  handler->block_count = 0;
  handler->after = after != FALSE;
  handler->closure = NULL;
573
  handler->has_invalid_closure_notify = 0;
574 575 576 577 578 579 580 581 582
  
  return handler;
}

static inline void
handler_ref (Handler *handler)
{
  g_return_if_fail (handler->ref_count > 0);
  
583
  handler->ref_count++;
584 585 586 587 588 589 590 591
}

static inline void
handler_unref_R (guint    signal_id,
		 gpointer instance,
		 Handler *handler)
{
  g_return_if_fail (handler->ref_count > 0);
592

593 594 595
  handler->ref_count--;

  if (G_UNLIKELY (handler->ref_count == 0))
596
    {
597 598
      HandlerList *hlist = NULL;

599 600
      if (handler->next)
        handler->next->prev = handler->prev;
601
      if (handler->prev)    /* watch out for g_signal_handlers_destroy()! */
602 603 604
        handler->prev->next = handler->next;
      else
        {
605
          hlist = handler_list_lookup (signal_id, instance);
606 607
          hlist->handlers = handler->next;
        }
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635

      if (instance)
        {
          /*  check if we are removing the handler pointed to by tail_before  */
          if (!handler->after && (!handler->next || handler->next->after))
            {
              if (!hlist)
                hlist = handler_list_lookup (signal_id, instance);
              if (hlist)
                {
                  g_assert (hlist->tail_before == handler); /* paranoid */
                  hlist->tail_before = handler->prev;
                }
            }

          /*  check if we are removing the handler pointed to by tail_after  */
          if (!handler->next)
            {
              if (!hlist)
                hlist = handler_list_lookup (signal_id, instance);
              if (hlist)
                {
                  g_assert (hlist->tail_after == handler); /* paranoid */
                  hlist->tail_after = handler->prev;
                }
            }
        }

636
      SIGNAL_UNLOCK ();
637
      g_closure_unref (handler->closure);
638
      SIGNAL_LOCK ();
639
      g_slice_free (Handler, handler);
640 641 642 643 644 645 646 647 648 649
    }
}

static void
handler_insert (guint    signal_id,
		gpointer instance,
		Handler  *handler)
{
  HandlerList *hlist;
  
650
  g_assert (handler->prev == NULL && handler->next == NULL); /* paranoid */
651 652 653 654 655
  
  hlist = handler_list_ensure (signal_id, instance);
  if (!hlist->handlers)
    {
      hlist->handlers = handler;
656 657 658 659 660 661 662
      if (!handler->after)
        hlist->tail_before = handler;
    }
  else if (handler->after)
    {
      handler->prev = hlist->tail_after;
      hlist->tail_after->next = handler;
663 664 665
    }
  else
    {
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
      if (hlist->tail_before)
        {
          handler->next = hlist->tail_before->next;
          if (handler->next)
            handler->next->prev = handler;
          handler->prev = hlist->tail_before;
          hlist->tail_before->next = handler;
        }
      else /* insert !after handler into a list of only after handlers */
        {
          handler->next = hlist->handlers;
          if (handler->next)
            handler->next->prev = handler;
          hlist->handlers = handler;
        }
      hlist->tail_before = handler;
682
    }
683 684 685

  if (!handler->next)
    hlist->tail_after = handler;
686 687
}

688 689 690 691 692 693 694 695
static void
node_update_single_va_closure (SignalNode *node)
{
  GClosure *closure = NULL;
  gboolean is_after = FALSE;

  /* Fast path single-handler without boxing the arguments in GValues */
  if (G_TYPE_IS_OBJECT (node->itype) &&
696
      (node->flags & (G_SIGNAL_MUST_COLLECT)) == 0 &&
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
      (node->emission_hooks == NULL || node->emission_hooks->hooks == NULL))
    {
      GSignalFlags run_type;
      ClassClosure * cc; 
      GBSearchArray *bsa = node->class_closure_bsa;

      if (bsa == NULL || bsa->n_nodes == 0)
	closure = SINGLE_VA_CLOSURE_EMPTY_MAGIC;
      else if (bsa->n_nodes == 1)
	{
	  /* Look for default class closure (can't support non-default as it
	     chains up using GValues */
	  cc = g_bsearch_array_get_nth (bsa, &g_class_closure_bconfig, 0);
	  if (cc->instance_type == 0)
	    {
	      run_type = node->flags & (G_SIGNAL_RUN_FIRST|G_SIGNAL_RUN_LAST|G_SIGNAL_RUN_CLEANUP);
	      /* Only support *one* of run-first or run-last, not multiple or cleanup */
	      if (run_type == G_SIGNAL_RUN_FIRST ||
		  run_type == G_SIGNAL_RUN_LAST)
		{
		  closure = cc->closure;
		  is_after = (run_type == G_SIGNAL_RUN_LAST);
		}
	    }
	}
    }

  node->single_va_closure_is_valid = TRUE;
  node->single_va_closure = closure;
  node->single_va_closure_is_after = is_after;
}

729
static inline void
730
emission_push (Emission  *emission)
Tim Janik's avatar
Tim Janik committed
731
{
732 733
  emission->next = g_emissions;
  g_emissions = emission;
734 735 736
}

static inline void
737
emission_pop (Emission  *emission)
738
{
Tim Janik's avatar
Tim Janik committed
739 740
  Emission *node, *last = NULL;

741
  for (node = g_emissions; node; last = node, node = last->next)
Tim Janik's avatar
Tim Janik committed
742 743 744 745 746
    if (node == emission)
      {
	if (last)
	  last->next = node->next;
	else
747
	  g_emissions = node->next;
Tim Janik's avatar
Tim Janik committed
748 749 750
	return;
      }
  g_assert_not_reached ();
751 752 753
}

static inline Emission*
754
emission_find (guint     signal_id,
755
	       GQuark    detail,
756 757 758 759
	       gpointer  instance)
{
  Emission *emission;
  
760
  for (emission = g_emissions; emission; emission = emission->next)
761
    if (emission->instance == instance &&
Tim Janik's avatar
Tim Janik committed
762 763 764 765 766 767 768
	emission->ihint.signal_id == signal_id &&
	emission->ihint.detail == detail)
      return emission;
  return NULL;
}

static inline Emission*
769
emission_find_innermost (gpointer instance)
Tim Janik's avatar
Tim Janik committed
770
{
771
  Emission *emission;
Tim Janik's avatar
Tim Janik committed
772
  
773
  for (emission = g_emissions; emission; emission = emission->next)
774
    if (emission->instance == instance)
775 776 777
      return emission;

  return NULL;
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
}

static gint
signal_key_cmp (gconstpointer node1,
                gconstpointer node2)
{
  const SignalKey *key1 = node1, *key2 = node2;
  
  if (key1->itype == key2->itype)
    return G_BSEARCH_ARRAY_CMP (key1->quark, key2->quark);
  else
    return G_BSEARCH_ARRAY_CMP (key1->itype, key2->itype);
}

void
793
_g_signal_init (void)
794
{
795
  SIGNAL_LOCK ();
796 797 798 799
  if (!g_n_signal_nodes)
    {
      /* setup handler list binary searchable array hash table (in german, that'd be one word ;) */
      g_handler_list_bsa_ht = g_hash_table_new (g_direct_hash, NULL);
800
      g_signal_key_bsa = g_bsearch_array_create (&g_signal_key_bconfig);
801 802 803 804 805 806
      
      /* invalid (0) signal_id */
      g_n_signal_nodes = 1;
      g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes);
      g_signal_nodes[0] = NULL;
    }
807
  SIGNAL_UNLOCK ();
808 809 810
}

void
811
_g_signals_destroy (GType itype)
812 813 814
{
  guint i;
  
815
  SIGNAL_LOCK ();
816
  for (i = 1; i < g_n_signal_nodes; i++)
817 818 819 820 821 822
    {
      SignalNode *node = g_signal_nodes[i];
      
      if (node->itype == itype)
        {
          if (node->destroyed)
823
            g_warning (G_STRLOC ": signal \"%s\" of type '%s' already destroyed",
824
                       node->name,
825
                       type_debug_name (node->itype));
826
          else
827
	    signal_destroy_R (node);
828 829
        }
    }
830
  SIGNAL_UNLOCK ();
831 832
}

Stefan Kost's avatar
Stefan Kost committed
833 834
/**
 * g_signal_stop_emission:
835
 * @instance: (type GObject.Object): the object whose signal handlers you wish to stop.
Stefan Kost's avatar
Stefan Kost committed
836 837
 * @signal_id: the signal identifier, as returned by g_signal_lookup().
 * @detail: the detail which the signal was emitted with.
838
 *
Stefan Kost's avatar
Stefan Kost committed
839
 * Stops a signal's current emission.
840
 *
Stefan Kost's avatar
Stefan Kost committed
841
 * This will prevent the default method from running, if the signal was
842
 * %G_SIGNAL_RUN_LAST and you connected normally (i.e. without the "after"
Stefan Kost's avatar
Stefan Kost committed
843
 * flag).
844
 *
Stefan Kost's avatar
Stefan Kost committed
845 846
 * Prints a warning if used on a signal which isn't being emitted.
 */
847 848
void
g_signal_stop_emission (gpointer instance,
849 850
                        guint    signal_id,
			GQuark   detail)
851 852 853 854 855 856
{
  SignalNode *node;
  
  g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
  g_return_if_fail (signal_id > 0);
  
857
  SIGNAL_LOCK ();
858
  node = LOOKUP_SIGNAL_NODE (signal_id);
859 860
  if (node && detail && !(node->flags & G_SIGNAL_DETAILED))
    {
861
      g_warning ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
862
      SIGNAL_UNLOCK ();
863 864
      return;
    }
865
  if (node && g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
866
    {
867
      Emission *emission = emission_find (signal_id, detail, instance);
868 869 870
      
      if (emission)
        {
Tim Janik's avatar
Tim Janik committed
871
          if (emission->state == EMISSION_HOOK)
872
            g_warning (G_STRLOC ": emission of signal \"%s\" for instance '%p' cannot be stopped from emission hook",
873
                       node->name, instance);
Tim Janik's avatar
Tim Janik committed
874 875
          else if (emission->state == EMISSION_RUN)
            emission->state = EMISSION_STOP;
876 877
        }
      else
878
        g_warning (G_STRLOC ": no emission of signal \"%s\" to stop for instance '%p'",
879 880 881
                   node->name, instance);
    }
  else
882
    g_warning ("%s: signal id '%u' is invalid for instance '%p'", G_STRLOC, signal_id, instance);
883
  SIGNAL_UNLOCK ();
884 885
}

886 887 888 889 890 891 892 893 894
static void
signal_finalize_hook (GHookList *hook_list,
		      GHook     *hook)
{
  GDestroyNotify destroy = hook->destroy;

  if (destroy)
    {
      hook->destroy = NULL;
895
      SIGNAL_UNLOCK ();
896
      destroy (hook->data);
897
      SIGNAL_LOCK ();
898 899 900
    }
}

Stefan Kost's avatar
Stefan Kost committed
901 902 903 904 905 906 907
/**
 * g_signal_add_emission_hook:
 * @signal_id: the signal identifier, as returned by g_signal_lookup().
 * @detail: the detail on which to call the hook.
 * @hook_func: a #GSignalEmissionHook function.
 * @hook_data: user data for @hook_func.
 * @data_destroy: a #GDestroyNotify for @hook_data.
908
 *
Stefan Kost's avatar
Stefan Kost committed
909 910 911
 * Adds an emission hook for a signal, which will get called for any emission
 * of that signal, independent of the instance. This is possible only
 * for signals which don't have #G_SIGNAL_NO_HOOKS flag set.
912
 *
Stefan Kost's avatar
Stefan Kost committed
913 914
 * Returns: the hook id, for later use with g_signal_remove_emission_hook().
 */
Tim Janik's avatar
Tim Janik committed
915
gulong
916 917 918 919 920 921
g_signal_add_emission_hook (guint               signal_id,
			    GQuark              detail,
			    GSignalEmissionHook hook_func,
			    gpointer            hook_data,
			    GDestroyNotify      data_destroy)
{
Tim Janik's avatar
Tim Janik committed
922
  static gulong seq_hook_id = 1;
923 924
  SignalNode *node;
  GHook *hook;
925
  SignalHook *signal_hook;
926 927 928 929

  g_return_val_if_fail (signal_id > 0, 0);
  g_return_val_if_fail (hook_func != NULL, 0);

930
  SIGNAL_LOCK ();
931
  node = LOOKUP_SIGNAL_NODE (signal_id);
932
  if (!node || node->destroyed)
933
    {
934
      g_warning ("%s: invalid signal id '%u'", G_STRLOC, signal_id);
935
      SIGNAL_UNLOCK ();
936 937
      return 0;
    }
938 939
  if (node->flags & G_SIGNAL_NO_HOOKS) 
    {
940
      g_warning ("%s: signal id '%u' does not support emission hooks (G_SIGNAL_NO_HOOKS flag set)", G_STRLOC, signal_id);
941 942 943
      SIGNAL_UNLOCK ();
      return 0;
    }
944 945
  if (detail && !(node->flags & G_SIGNAL_DETAILED))
    {
946
      g_warning ("%s: signal id '%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
947
      SIGNAL_UNLOCK ();
948 949
      return 0;
    }
950
    node->single_va_closure_is_valid = FALSE;
951 952 953
  if (!node->emission_hooks)
    {
      node->emission_hooks = g_new (GHookList, 1);
954
      g_hook_list_init (node->emission_hooks, sizeof (SignalHook));
955 956
      node->emission_hooks->finalize_hook = signal_finalize_hook;
    }
Marc-André Lureau's avatar
Marc-André Lureau committed
957 958 959

  node_check_deprecated (node);

960 961
  hook = g_hook_alloc (node->emission_hooks);
  hook->data = hook_data;
962
  hook->func = (gpointer) hook_func;
963
  hook->destroy = data_destroy;
964 965
  signal_hook = SIGNAL_HOOK (hook);
  signal_hook->detail = detail;
966 967 968
  node->emission_hooks->seq_id = seq_hook_id;
  g_hook_append (node->emission_hooks, hook);
  seq_hook_id = node->emission_hooks->seq_id;
969

970
  SIGNAL_UNLOCK ();
971 972 973 974

  return hook->hook_id;
}

Stefan Kost's avatar
Stefan Kost committed
975 976 977
/**
 * g_signal_remove_emission_hook:
 * @signal_id: the id of the signal
978
 * @hook_id: the id of the emission hook, as returned by
Stefan Kost's avatar
Stefan Kost committed
979
 *  g_signal_add_emission_hook()
980
 *
Stefan Kost's avatar
Stefan Kost committed
981 982
 * Deletes an emission hook.
 */
983
void
Tim Janik's avatar
Tim Janik committed
984 985
g_signal_remove_emission_hook (guint  signal_id,
			       gulong hook_id)
986 987 988 989 990 991
{
  SignalNode *node;

  g_return_if_fail (signal_id > 0);
  g_return_if_fail (hook_id > 0);

992
  SIGNAL_LOCK ();
993 994
  node = LOOKUP_SIGNAL_NODE (signal_id);
  if (!node || node->destroyed)
995
    {
996
      g_warning ("%s: invalid signal id '%u'", G_STRLOC, signal_id);
997 998
      goto out;
    }
999
  else if (!node->emission_hooks || !g_hook_destroy (node->emission_hooks, hook_id))
Tim Janik's avatar
Tim Janik committed
1000
    g_warning ("%s: signal \"%s\" had no hook (%lu) to remove", G_STRLOC, node->name, hook_id);
1001 1002 1003

  node->single_va_closure_is_valid = FALSE;

1004
 out:
1005
  SIGNAL_UNLOCK ();
1006 1007
}

1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
static inline guint
signal_parse_name (const gchar *name,
		   GType        itype,
		   GQuark      *detail_p,
		   gboolean     force_quark)
{
  const gchar *colon = strchr (name, ':');
  guint signal_id;
  
  if (!colon)
    {
      signal_id = signal_id_lookup (g_quark_try_string (name), itype);
      if (signal_id && detail_p)
	*detail_p = 0;
    }
  else if (colon[1] == ':')
    {
      gchar buffer[32];
      guint l = colon - name;
      
      if (l < 32)
	{
	  memcpy (buffer, name, l);
	  buffer[l] = 0;
	  signal_id = signal_id_lookup (g_quark_try_string (buffer), itype);
	}
      else
	{
	  gchar *signal = g_new (gchar, l + 1);
	  
	  memcpy (signal, name, l);
	  signal[l] = 0;
	  signal_id = signal_id_lookup (g_quark_try_string (signal), itype);
	  g_free (signal);
	}
      
      if (signal_id && detail_p)
	*detail_p = colon[2] ? (force_quark ? g_quark_from_string : g_quark_try_string) (colon + 2) : 0;
    }
  else
    signal_id = 0;
  return signal_id;
}

Stefan Kost's avatar
Stefan Kost committed
1052 1053 1054 1055
/**
 * g_signal_parse_name:
 * @detailed_signal: a string of the form "signal-name::detail".
 * @itype: The interface/instance type that introduced "signal-name".
1056 1057
 * @signal_id_p: (out): Location to store the signal id.
 * @detail_p: (out): Location to store the detail quark.
Stefan Kost's avatar
Stefan Kost committed
1058
 * @force_detail_quark: %TRUE forces creation of a #GQuark for the detail.
1059
 *
Stefan Kost's avatar
Stefan Kost committed
1060 1061
 * Internal function to parse a signal name into its @signal_id
 * and @detail quark.
1062
 *
Stefan Kost's avatar
Stefan Kost committed
1063 1064
 * Returns: Whether the signal name could successfully be parsed and @signal_id_p and @detail_p contain valid return values.
 */
1065 1066 1067 1068 1069 1070 1071
gboolean
g_signal_parse_name (const gchar *detailed_signal,
		     GType        itype,
		     guint       *signal_id_p,
		     GQuark      *detail_p,
		     gboolean	  force_detail_quark)
{
1072
  SignalNode *node;
1073 1074
  GQuark detail = 0;
  guint signal_id;
1075
  
1076 1077
  g_return_val_if_fail (detailed_signal != NULL, FALSE);
  g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), FALSE);
1078
  
1079
  SIGNAL_LOCK ();
1080
  signal_id = signal_parse_name (detailed_signal, itype, &detail, force_detail_quark);
1081
  SIGNAL_UNLOCK ();
1082 1083 1084 1085

  node = signal_id ? LOOKUP_SIGNAL_NODE (signal_id) : NULL;
  if (!node || node->destroyed ||
      (detail && !(node->flags & G_SIGNAL_DETAILED)))
1086
    return FALSE;
1087 1088 1089 1090 1091 1092 1093

  if (signal_id_p)
    *signal_id_p = signal_id;
  if (detail_p)
    *detail_p = detail;
  
  return TRUE;
1094 1095
}

Stefan Kost's avatar
Stefan Kost committed
1096 1097
/**
 * g_signal_stop_emission_by_name:
1098
 * @instance: (type GObject.Object): the object whose signal handlers you wish to stop.
Stefan Kost's avatar
Stefan Kost committed
1099
 * @detailed_signal: a string of the form "signal-name::detail".
1100
 *
Stefan Kost's avatar
Stefan Kost committed
1101
 * Stops a signal's current emission.
1102 1103
 *
 * This is just like g_signal_stop_emission() except it will look up the
Stefan Kost's avatar
Stefan Kost committed
1104 1105
 * signal id for you.
 */
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
void
g_signal_stop_emission_by_name (gpointer     instance,
				const gchar *detailed_signal)
{
  guint signal_id;
  GQuark detail = 0;
  GType itype;
  
  g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
  g_return_if_fail (detailed_signal != NULL);
  
1117
  SIGNAL_LOCK ();
1118 1119 1120 1121 1122 1123 1124
  itype = G_TYPE_FROM_INSTANCE (instance);
  signal_id = signal_parse_name (detailed_signal, itype, &detail, TRUE);
  if (signal_id)
    {
      SignalNode *node = LOOKUP_SIGNAL_NODE (signal_id);
      
      if (detail && !(node->flags & G_SIGNAL_DETAILED))
1125
	g_warning ("%s: signal '%s' does not support details", G_STRLOC, detailed_signal);
1126
      else if (!g_type_is_a (itype, node->itype))
1127
        g_warning ("%s: signal '%s' is invalid for instance '%p' of type '%s'",
1128
                   G_STRLOC, detailed_signal, instance, g_type_name (itype));
1129 1130
      else
	{
1131
	  Emission *emission = emission_find (signal_id, detail, instance);
1132 1133 1134
	  
	  if (emission)
	    {
Tim Janik's avatar
Tim Janik committed
1135
	      if (emission->state == EMISSION_HOOK)
1136
		g_warning (G_STRLOC ": emission of signal \"%s\" for instance '%p' cannot be stopped from emission hook",
1137
			   node->name, instance);
Tim Janik's avatar
Tim Janik committed
1138 1139
	      else if (emission->state == EMISSION_RUN)
		emission->state = EMISSION_STOP;
1140 1141
	    }
	  else
1142
	    g_warning (G_STRLOC ": no emission of signal \"%s\" to stop for instance '%p'",
1143 1144 1145 1146
		       node->name, instance);
	}
    }
  else
1147
    g_warning ("%s: signal '%s' is invalid for instance '%p' of type '%s'",
1148
               G_STRLOC, detailed_signal, instance, g_type_name (itype));
1149
  SIGNAL_UNLOCK ();
1150 1151
}

Stefan Kost's avatar
Stefan Kost committed
1152 1153 1154 1155
/**
 * g_signal_lookup:
 * @name: the signal's name.
 * @itype: the type that the signal operates on.
1156 1157 1158
 *
 * Given the name of the signal and the type of object it connects to, gets
 * the signal's identifying integer. Emitting the signal by number is
Stefan Kost's avatar
Stefan Kost committed
1159
 * somewhat faster than using the name each time.
1160
 *
Stefan Kost's avatar
Stefan Kost committed
1161
 * Also tries the ancestors of the given type.
1162
 *
Stefan Kost's avatar
Stefan Kost committed
1163
 * See g_signal_new() for details on allowed signal names.
1164
 *
Stefan Kost's avatar
Stefan Kost committed
1165 1166
 * Returns: the signal's identifying number, or 0 if no signal was found.
 */
1167 1168 1169 1170
guint
g_signal_lookup (const gchar *name,
                 GType        itype)
{
1171
  guint signal_id;
1172 1173 1174
  g_return_val_if_fail (name != NULL, 0);
  g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), 0);
  
1175
  SIGNAL_LOCK ();
1176
  signal_id = signal_id_lookup (g_quark_try_string (name), itype);
1177
  SIGNAL_UNLOCK ();
1178 1179 1180 1181
  if (!signal_id)
    {
      /* give elaborate warnings */
      if (!g_type_name (itype))
1182
	g_warning (G_STRLOC ": unable to lookup signal \"%s\" for invalid type id '%"G_GSIZE_FORMAT"'",
1183 1184
		   name, itype);
      else if (!G_TYPE_IS_INSTANTIATABLE (itype))
1185
	g_warning (G_STRLOC ": unable to lookup signal \"%s\" for non instantiatable type '%s'",
1186 1187
		   name, g_type_name (itype));
      else if (!g_type_class_peek (itype))
1188
	g_warning (G_STRLOC ": unable to lookup signal \"%s\" of unloaded type '%s'",
1189 1190
		   name, g_type_name (itype));
    }
1191
  
1192
  return signal_id;
1193 1194
}

Stefan Kost's avatar
Stefan Kost committed
1195 1196 1197 1198
/**
 * g_signal_list_ids:
 * @itype: Instance or interface type.
 * @n_ids: Location to store the number of signal ids for @itype.
1199
 *
Stefan Kost's avatar
Stefan Kost committed
1200 1201 1202
 * Lists the signals by id that a certain instance or interface type
 * created. Further information about the signals can be acquired through
 * g_signal_query().
1203
 *
1204
 * Returns: (array length=n_ids): Newly allocated array of signal IDs.
Stefan Kost's avatar
Stefan Kost committed
1205
 */
1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218
guint*
g_signal_list_ids (GType  itype,
		   guint *n_ids)
{
  SignalKey *keys;
  GArray *result;
  guint n_nodes;
  guint i;
  
  g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), NULL);
  g_return_val_if_fail (n_ids != NULL, NULL);
  
  SIGNAL_LOCK ();
1219 1220
  keys = g_bsearch_array_get_nth (g_signal_key_bsa, &g_signal_key_bconfig, 0);
  n_nodes = g_bsearch_array_get_n_nodes (g_signal_key_bsa);
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239
  result = g_array_new (FALSE, FALSE, sizeof (guint));
  
  for (i = 0; i < n_nodes; i++)
    if (keys[i].itype == itype)
      {
	const gchar *name = g_quark_to_string (keys[i].quark);
	
	/* Signal names with "_" in them are aliases to the same
	 * name with "-" instead of "_".
	 */
	if (!strchr (name, '_'))
	  g_array_append_val (result, keys[i].signal_id);
      }
  *n_ids = result->len;
  SIGNAL_UNLOCK ();
  if (!n_nodes)
    {
      /* give elaborate warnings */
      if (!g_type_name (itype))
1240
	g_warning (G_STRLOC ": unable to list signals for invalid type id '%"G_GSIZE_FORMAT"'",
1241
		   itype);
1242
      else if (!G_TYPE_IS_INSTANTIATABLE (itype) && !G_TYPE_IS_INTERFACE (itype))
1243
	g_warning (G_STRLOC ": unable to list signals of non instantiatable type '%s'",
1244
		   g_type_name (itype));
1245
      else if (!g_type_class_peek (itype) && !G_TYPE_IS_INTERFACE (itype))
1246
	g_warning (G_STRLOC ": unable to list signals of unloaded type '%s'",
1247 1248 1249 1250 1251 1252
		   g_type_name (itype));
    }
  
  return (guint*) g_array_free (result, FALSE);
}

Stefan Kost's avatar
Stefan Kost committed
1253 1254 1255
/**
 * g_signal_name:
 * @signal_id: the signal's identifying number.
1256
 *
Stefan Kost's avatar
Stefan Kost committed
1257
 * Given the signal's identifier, finds its name.