weblog d’un abbe

20080216

Using accumulator to prevent signals from continuing further

Filed under: Hacking — abbe @ 0416
// How to use accumulator to control signal
// delivery in GLib2
// (C) 2007. Ashish Shukla

#include <glib-object.h>
#include <glib.h>
#include <glib/gprintf.h>

G_BEGIN_DECLS

#define TEST_OBJECT_TYPE                 (test_object_get_type())
#define TEST_OBJECT(obj)                 (G_TYPE_CHECK_INSTANCE_CAST((obj), TEST_OBJECT_TYPE, TestObject))
#define TEST_OBJECT_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST((klass), TEST_OBJECT_TYPE, TestObjectClass))
#define TEST_IS_OBJECT(obj)              (G_TYPE_CHECK_INSTANCE_TYPE(obj, TEST_OBJECT_TYPE))
#define TEST_IS_OBJECT_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE(klass, TEST_OBJECT_TYPE))
#define TEST_IS_OBJECT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), TEST_OBJECT_TYPE, TestObjectClass))

typedef struct _TestObject TestObject;
typedef struct _TestObjectClass TestObjectClass;

struct _TestObjectClass {
  GObjectClass parent;

  guint sig_test;
};

struct _TestObject {
  GObject parent;
};

G_END_DECLS

G_DEFINE_TYPE(TestObject, test_object, G_TYPE_OBJECT) ;

static GObject*
test_object_constructor(GType type,
			guint n_props,
			GObjectConstructParam* props)
{
  TestObjectClass* klass;
  GObjectClass* pc;

  g_printf("Test Object constructor()\n");

  // dispatch the parent class constructor.
  klass = TEST_OBJECT_CLASS(g_type_class_peek(TEST_OBJECT_TYPE));
  pc = G_OBJECT_CLASS(g_type_class_peek_parent(klass));
  
  return pc->constructor(type, n_props, props);
}

static gboolean
test_accumulator(GSignalInvocationHint* hint, GValue* return_accumulator,
		 const GValue* return_handler, gpointer data)
{
  g_printf("closure %d called\n", hint->detail);

  hint->detail++;

  // only allow 3 signal invocations
  if(hint->detail >= 3 )
    {
      g_printf("signal aborted\n");
      return FALSE;
    }

  return TRUE;
}

static void
test_object_trigger_test(TestObject* object)
{
  g_printf("Triggering test on %p\n", object);
  g_signal_emit(object, TEST_IS_OBJECT_GET_CLASS(object)->sig_test, 0);
}

static void
test_object_class_init(TestObjectClass* klass)
{
  GObjectClass* object_class;

  object_class = G_OBJECT_CLASS(klass);
  object_class->constructor = test_object_constructor;

  klass->sig_test = g_signal_new("test", G_TYPE_FROM_CLASS(klass),
				 G_SIGNAL_RUN_LAST, 0,
				 &test_accumulator, NULL,
				 g_cclosure_marshal_VOID__VOID,
				 G_TYPE_INT, 0, NULL);

  g_printf("In class_init() routine, registered signal id: %d\n", klass->sig_test);
}

static void
test_object_init(TestObject* self)
{
  g_printf("Init test_object_init() routine\n");
}

#define DEFINE_TEST_SIG_HANDLER(x) \
  static int test_handler##x(void) { g_printf("In handler " #x "\n"); return x; }

DEFINE_TEST_SIG_HANDLER(0)
DEFINE_TEST_SIG_HANDLER(1)
DEFINE_TEST_SIG_HANDLER(2)
DEFINE_TEST_SIG_HANDLER(3)
DEFINE_TEST_SIG_HANDLER(4)

int
main()
{
  TestObject* object;

  g_type_init();

  object = g_object_new(TEST_OBJECT_TYPE, NULL);

  g_printf("created.\n");

  g_signal_connect(object, "test", test_handler0, NULL);
  g_signal_connect(object, "test", test_handler1, NULL);
  g_signal_connect(object, "test", test_handler2, NULL);
  g_signal_connect(object, "test", test_handler3, NULL);
  g_signal_connect(object, "test", test_handler4, NULL);

  test_object_trigger_test(object);

  g_object_unref(object);


  g_printf("un-reffed\n");

  return 0;
}

Outputs:

abbe [gobject] chateau $ ./test
In class_init() routine, registered signal id: 2
Test Object constructor()
Init test_object_init() routine
created.
Triggering test on 0x607020
In handler 0
closure 0 called
In handler 1
closure 1 called
In handler 2
closure 2 called
signal aborted
un-reffed
Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: