天天看點

定義自己的g_signal

       對于glib,我們經常用到信号通知機制來觸發事件,在GObject中,可以自己定義signal信号,用于事件觸發中。

       一般在class_init時,由g_signal_new配合一些參數生成一個新信号句柄,然後使用g_signal_connect連接配接對象和處理方式(回調函數),最後由g_signal_emit發出信号觸發。

一,其中最關鍵的是就g_signal_new,在這個過程中,我們應該先了解下相關的參數,其一般模型如下:

guint

g_signal_new (const gchar     *signal_name,

             GType          itype,

             GSignalFlags        signal_flags,

             guint               class_offset,

             GSignalAccumulator  accumulator,

             gpointer               accu_data,

             GSignalCMarshaller  c_marshaller,

             GType          return_type,

             guint            n_params,

             ...)

通過上面的函數模型可以看出,其參數個數是可變的。而這些參數有些是必須的,有些可設為NULL,下面可以結合一個基于clutter的執行個體來解釋一下各個參數的含義(SECTION 1和2是從clutter源代碼中提取出來的定義示例):

1,            const gchar *signal_name: 該參數是定義信号的名字,它由分隔符以及ASCII碼中的字母和數字構成,且第一個字元必須是字母,執行個體中定義的名字為"myself-signal"。(分隔符可以是"-"或"_"——事實上,系統會先調用g_strdelimit把"_"轉化為"-"再存儲signal_name。是以,在調用g_singal_emit_by_name時,detailed_signal參數中的分隔符必須是"-");

2,            GType itype:該參數是signal所依附的類的在GType類型系統中注冊時得到的ID,也就是*_get_type()函數的傳回值。而在clutter工程中,class_init時可使用G_OBJECT_CLASS_TYPE(klass)來獲得,執行個體中使用CLUTTER_TYPE_ACTOR擷取。(clutter _actor應該才是stage和texture、text的基類);

3,           GSignalFlags signal_flags:該參數是信号的屬性标記,共有七種,其中有提到"per-object handler",将在下面class_offset中介紹:

· G_SIGNAL_RUN_FIRST:調用回調函數時,"per-object handler"對應的回調函數将第一個調用;

· G_SIGNAL_RUN_LAST:調用回調函數時,"per-object handler"對應的回調函數将在使用者用g_signal_connect連接配接的回調函數之後調用,并在使用者用g_signal_connect _after連接配接的回調函數之前調用;

· G_SIGNAL_RUN_CLEANUP:調用回調函數時,"per-object handler"對應的回調函數将最後一個調用;

· G_SIGNAL_NO_RECURSE:信号發射時,如果信号的上次發射還沒有結束,那麼本次信号發射将不再進行,而隻是使上次的信号發射重新開始。[wyj1] 

· G_SIGNAL_DETAILED:信号名字可以使用"signal_name::detailed"的形式。

· G_SIGNAL_ACTION:程式員可以在代碼中自由地調用g_signal_emit族的函數來發射信号,而不需要把g_signal_emit族的函數放在一段代碼中再來調用。

· G_SIGNAL_NO_HOOKS:信号發射過程中不支援鈎子函數。

4,            guint class_offset:該參數是itype對應的類的class結構中的一個函數指針相對于class結構的執行個體的首位址的偏移量。該函數指針所對應的函數常被稱為"per-object handler","default (signal) handler"或"object methond handler",并将在信号發出後被調用(如調用g_signal_emit_by_name)。常配合宏G_STRUCT_OFFSET使用(該宏能夠傳回結構體變量的成員相對于該結構體的變量的首位址的偏移量)。如SECTION1中,G_STRUCT_OFFSET (ClutterActorClass, paint),就是指在clutterActorClass類中,"per-object handler”為 paint的函數的偏移位址。而如果将該參數設為0,則表示該類沒有"per-object handler"。執行個體中由于在類中沒有定義相應的"per-object handler”,故設為0;

5,            GSignalAccumulator accumulator:該參數是一個函數指針,其對應的函數将在該信号的每個handler執行完以後執行。其函數模型可參見SECTION 3。該函數的傳回類型為gboolean。如果其傳回值為FASLE,則signal發射過程就會被中止(即不再調用後面的hander),否則會繼續下去。事實上,"delete-event"等帶有event字尾的signal就是利用了這一點——這些信号的某個回調函數如果傳回了TRUE,則以後的回調函數就不會被調用。我的了解是,此函數的功能就是決定信号還要不要繼續往下走?(注意,如果該signal有accumulator,則回調函數類型(由c_marshaller反映)必須有傳回值,否則該signal不能有accumulator,也即在調用g_signal_new時以NULL作為該形參的實參)

6,           gpointer accu_data:該參數将作為使用者自定義參數傳入accumulator所指向的函數中。

7,            GSignalCMarshaller c_marshaller:該參數是一個GSignalCMarshall類型的函數指針,其值反映了回調函數的傳回值類型和額外參數類型(所謂“額外參數”,即指除回調函數中instance和user_data以外的參數)。

l             例如,g_closure_marshal_VOID_VOID說明該signal的回調函數為以下的callback類型:typedef void (*callback)  (gpointer instance, gpointer user_data);

l             g_closure_marshal_VOID_POINTER----------typedef void (*callback)  (gpointer instance, gpointer arg1, gpointer user_data);

l             g_cclosure_marshal_VOID__CHAR ()---------void (*callback) (gpointer instance, gchar arg1, gpointer user_data);

l             g_cclosure_marshal_VOID__INT ()-----------void (*callback) (gpointer instance, gint arg1, gpointer user_data);

l             以前列舉了部分,可參考http://developer.gnome.org/gobject/unstable/gobject-Closures.html。如果預設提供的GClosureMarshall中沒有你需要的,你可以用glib-genmarshall生成它,具體可參見devhelp中有關glib-genmarshall的說明。如: 為信号生成一個參數轉換函數為custom_marshal_VOID__OBJECT:

[[email protected] ~]$:cat marshal.list   

VOID:OBJECT     

[[email protected] ~]$:glib-genmarshal --header --prefix=custom_marshal marshal.list > custom_marshal.h   

[[email protected] ~]$:glib-genmarshal --header --prefix=custom_marshal marshal.list > custom_marshal.c 

8,            GType return_type:該參數的值應為回調函數的傳回值在GType類型系統中的ID。G_TYPE_NONE表示沒有傳回值,G_TYPE_BOOLEAN表示傳回值為gboolean;

9,            guint n_params:該參數的值應為回調函數的額外參數的個數,如SECTION 1為0,則表示後面沒有參數,SECTION 2為1,表示後面還有一個參數。

二,定義好一個signal後,就是要将該信号與對象object和回調函數callback關聯起來,使用g_signal_connect或者g_signal_connect_after函數。

#define g_signal_connect(instance, detailed_signal, c_handler, data) \

g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)

#define g_signal_connect_after(instance, detailed_signal, c_handler, data) \

    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_AFTER)

執行個體中如下:

       g_signal_connect (stage, "key-press-event", G_CALLBACK (_keyboard_cb),  (gpointer)text);

       g_signal_connect (texture, "myself-signal", G_CALLBACK (on_paint),  NULL);

三,最後就是發射相關信号觸發事件。

       兩種信号發射方式:

1,使用signal name: g_signal_emit_by_name(texture, "myself-signal");

2,使用signal handle: g_signal_emit(texture, mysignal, 0);

clutter執行個體:

#include <clutter/clutter.h>

ClutterActor *stage, *texture;

gint mysignal;

static void

on_paint (ClutterActor *actor, gconstpointer *data)

{

  printf("on paint\n");

}

static void

_keyboard_cb(ClutterActor *actor, ClutterEvent * event, gpointer *data)

{

       printf("keyboard cb\n");

       g_signal_emit_by_name(texture, "myself-signal");

       // g_signal_emit(texture1, mysignal, 0);

}

gint main(gint argc, gchar *argv[])

{

clutter_init (&argc, &argv);

       stage = clutter_stage_get_default ();

       clutter_actor_set_size (stage, 1280, 720);

       g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);

       GType type = CLUTTER_TYPE_ACTOR;

       printf("type=%d\n", type);

       texture = clutter_texture_new_from_file("/res/play.png",NULL);

       clutter_actor_set_position(texture,100,230);

       texture1 = clutter_texture_new_from_file("/res/play.png",NULL);

       clutter_actor_set_position(texture1,100,330);

       text = clutter_text_new_with_text ("sans 22",   "hello");

       clutter_actor_set_position(text,100,130);

       clutter_text_set_color(CLUTTER_TEXT(text), &text_color);

       ClutterActorClass *klass;

       GObjectClass *object_class;

       klass = CLUTTER_ACTOR_GET_CLASS (…);

object_class = G_OBJECT_CLASS (klass);

mysignal = g_signal_new ("myself-signal",

               CLUTTER_TYPE_ACTOR, //G_TYPE_FROM_CLASS (object_class),

                G_SIGNAL_RUN_LAST,

                0,

                NULL, NULL,

                g_cclosure_marshal_VOID__VOID,

                G_TYPE_NONE, 0

                );

       g_signal_connect (stage, "key-press-event", G_CALLBACK (_keyboard_cb),  text);

       g_signal_connect (texture, "myself-signal", G_CALLBACK (on_paint),  NULL);

       clutter_container_add (CLUTTER_CONTAINER (stage), texture,   NULL);

}

SECTION 1:

actor_signals[PAINT] =

    g_signal_new (I_("paint"),

                  G_TYPE_FROM_CLASS (object_class),

                  G_SIGNAL_RUN_LAST,

                  G_STRUCT_OFFSET (ClutterActorClass, paint),

                  NULL, NULL,

                  _clutter_marshal_VOID__VOID,

                  G_TYPE_NONE, 0);

SECTION 2:

  actor_signals[KEY_PRESS_EVENT] =

    g_signal_new (I_("key-press-event"),

                     G_TYPE_FROM_CLASS (object_class),

                     G_SIGNAL_RUN_LAST,

                     G_STRUCT_OFFSET (ClutterActorClass, key_press_event),

                     _clutter_boolean_handled_accumulator, NULL,

                     _clutter_marshal_BOOLEAN__BOXED,

                     G_TYPE_BOOLEAN, 1,

                     CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);

SECTION 3:

gboolean

_clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,

                                      GValue                *return_accu,

                                      const GValue          *handler_return,

                                      gpointer               dummy)

{

  gboolean continue_emission;

  gboolean signal_handled;

  signal_handled = g_value_get_boolean (handler_return);

  g_value_set_boolean (return_accu, signal_handled);

  continue_emission = !signal_handled;

  return continue_emission;

}

繼續閱讀