Add support for emitting GObject signals dynamically
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Tue, 7 Apr 2009 07:12:31 +0000 (09:12 +0200)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Tue, 7 Apr 2009 07:12:31 +0000 (09:12 +0200)
This will be needed later to call the action signals of
playbin2 for example.

Also get the correct instance size of GClosure at runtime
instead of always taking the largest known one.

gstreamer-sharp/DynamicSignal.cs
gstreamer-sharp/glue/Makefile.am
gstreamer-sharp/glue/dynamicsignal.c [new file with mode: 0644]

index 0fadbe6..63625aa 100644 (file)
@@ -1,9 +1,10 @@
 //
 //
-// (C) 2006 Novell Inc.
+// Copyright (C) 2006 Novell Inc.
+// Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
 //
-// This class implements the functionalities to bind a callback
-// function to a signal dynamically.  
+// This class implements functions to bind callbacks to GObject signals
+// dynamically and to emit signals dynamically.
 // 
 //
 
@@ -23,18 +24,18 @@ namespace Gst {
                
                private static readonly int gvalue_struct_size = Marshal.SizeOf(typeof(GLib.Value));
 
-               class Key {
+               class ObjectSignalKey {
                        object o;
                        string signal_name;
 
-                       public Key (object o, string name) {
+                       public ObjectSignalKey (object o, string name) {
                                this.o = o;
                                signal_name = name;
                        }
 
                        public override bool Equals(object o) {
-                               if(o is Key) {
-                                       Key k = (Key) o;
+                               if(o is ObjectSignalKey) {
+                                       ObjectSignalKey k = (ObjectSignalKey) o;
                                        return k.o.Equals(this.o) && signal_name.Equals(k.signal_name);
                                }
                                return base.Equals(o);
@@ -80,12 +81,14 @@ namespace Gst {
                        Connect(o, name, false, handler);
                }
 
+               static int g_closure_sizeof = gstsharp_g_closure_sizeof ();
+
                public static void Connect(GLib.Object o, string name, 
                        bool after, DynamicSignalHandler handler)
                {
                        Delegate newHandler;
 
-                       Key k = new Key(o, name);
+                       ObjectSignalKey k = new ObjectSignalKey(o, name);
                                                
                        if(SignalHandlers[k] != null) {
                                SignalInfo si = (SignalInfo) SignalHandlers[k];
@@ -93,17 +96,18 @@ namespace Gst {
                                si.RegisteredHandler = newHandler;
                        }
                        else {
-                               /* We use 32 as this is correct for 32 bit and 64 bit architectures */
-                               IntPtr closure = g_closure_new_simple(32, IntPtr.Zero);
+                               IntPtr closure = g_closure_new_simple(g_closure_sizeof, IntPtr.Zero);
                                g_closure_set_meta_marshal(closure, (IntPtr) GCHandle.Alloc(k),  marshalHandler);
                                uint signalId = g_signal_connect_closure(o.Handle, name, closure, after); 
                                SignalHandlers.Add(k, new SignalInfo(signalId, closure, handler));
                        }
                }
 
+               [DllImport("gstreamersharpglue-0.10")]
+               static extern int gstsharp_g_closure_sizeof ();
 
                public static void Disconnect(GLib.Object o, string name, DynamicSignalHandler handler) {
-                       Key k = new Key(o, name);
+                       ObjectSignalKey k = new ObjectSignalKey(o, name);
                        if(SignalHandlers[k] != null) 
                        {
                                SignalInfo si = (SignalInfo) SignalHandlers[k];
@@ -142,7 +146,7 @@ namespace Gst {
                                Console.Error.WriteLine("No available data");
                        }
 
-                       Key k = (Key)((GCHandle) data).Target;
+                       ObjectSignalKey k = (ObjectSignalKey)((GCHandle) data).Target;
                        if(k != null) {
                                SignalArgs arg = new SignalArgs();
                                arg.Args = args;
@@ -166,7 +170,148 @@ namespace Gst {
                [DllImport("gobject-2.0.dll")]
                static extern void g_closure_set_meta_marshal(IntPtr closure, IntPtr data, GClosureMarshal marshal);
 
+               class GTypeSignalKey {
+                       GType type;
+                       string signal_name;
+
+                       public GTypeSignalKey (GType type, string name) {
+                               this.type = type;
+                               signal_name = name;
+                       }
+
+                       public override bool Equals(object o) {
+                               if(o is GTypeSignalKey) {
+                                       GTypeSignalKey k = (GTypeSignalKey) o;
+                                       return k.type.Equals(this.type) && signal_name.Equals(k.signal_name);
+                               }
+                               return base.Equals(o);
+                       }
+
+                       public override int GetHashCode() {
+                               return type.GetHashCode() ^ signal_name.GetHashCode();
+                       }
+               }
+
+               struct SignalQuery {
+                       public uint signal_id;
+                       public string signal_name;
+                       public GType itype;
+                       public uint signal_flags;
+                       public GType return_type;
+                       public uint n_params;
+                       public Type[] param_types;
+               }
+
+               static Hashtable SignalEmitInfo = new Hashtable ();
+
+               public static object Emit (GLib.Object o, string name, params object[] parameters)
+               {
+                       SignalQuery query;
+                       IntPtr type = gstsharp_g_type_from_instance (o.Handle);                 
+                       GType gtype = new GType (type);
+                       string signal_name, signal_detail;
+                       uint signal_detail_quark = 0;
+                       int colon;
+                       
+                       colon = name.LastIndexOf ("::");
+                       
+                       if (colon == -1) {
+                               signal_name = name;
+                               signal_detail = String.Empty;
+                       } else {
+                               signal_name = name.Substring (0, colon);
+                               signal_detail = name.Substring (colon + 2);
+                       }
+                       
+                       GTypeSignalKey key = new GTypeSignalKey (gtype, signal_name);
+                       
+                       if (SignalEmitInfo[key] == null) {
+                               uint signal_id = g_signal_lookup (signal_name, type);
+                               
+                               if (signal_id == 0)
+                                       throw new NotSupportedException (String.Format ("{0} has no signal of name {1}", o, name));
+                               GSignalQuery q = new GSignalQuery ();
+                               g_signal_query (signal_id, ref q);
+                               
+                               if (q.signal_id == 0)
+                                       throw new NotSupportedException (String.Format ("{0} couldn't be queried for signal with name {1}", o, name));
+                               
+                               query = new SignalQuery ();
+                               
+                               query.signal_id = signal_id;
+                               query.signal_name = Marshaller.Utf8PtrToString (q.signal_name);
+                               query.itype = new GType (q.itype);
+                               query.signal_flags = q.signal_flags;
+                               query.return_type = new GType (q.return_type);
+                               query.n_params = q.n_params;
+                               query.param_types = new Type[q.n_params];
+                               
+                               for (int i = 0; i < query.n_params; i++) {
+                                       IntPtr t = Marshal.ReadIntPtr (q.param_types, i);
+                                       GType g = new GType (t);
+                                       
+                                       query.param_types[i] = (Type) g;
+                               }
+                               
+                               SignalEmitInfo.Add (key, query);
+                       }
+                       
+                       query = (SignalQuery) SignalEmitInfo[key];
+                       GLib.Value[] signal_parameters = new GLib.Value[query.n_params + 1];
+                       signal_parameters[0] = new GLib.Value (o);
+                       
+                       if (parameters.Length != query.n_params)
+                               throw new ApplicationException (String.Format ("Invalid number of parameters: expected {0}, got {1}", query.n_params, parameters.Length));
+                       
+                       for (int i = 0; i < query.n_params; i++) {
+                               Type expected_type = (Type) query.param_types[i];
+                               Type given_type = parameters[i].GetType ();
+                               
+                               if (expected_type != given_type && ! given_type.IsSubclassOf (given_type))
+                                       throw new ApplicationException (String.Format ("Invalid parameter type: expected {0}, got {1}", expected_type, given_type));
+                               
+                               signal_parameters[i + 1] = new GLib.Value (parameters[i]);
+                       }
+                       
+                       GLib.Value return_value = new GLib.Value ();
+                       if (query.return_type != GType.Invalid && query.return_type != GType.None)
+                               return_value.Init (query.return_type);
+                       
+                       if (signal_detail != String.Empty)
+                               signal_detail_quark = g_quark_from_string (signal_detail);
+                       
+                       g_signal_emitv (signal_parameters, query.signal_id, signal_detail_quark, ref return_value);
+
+                       return (query.return_type != GType.Invalid && query.return_type != GType.None) ? return_value.Val : null;
+               }
+
+               [DllImport ("gstreamersharpglue-0.10")]
+               static extern IntPtr gstsharp_g_type_from_instance (IntPtr o);
+
                [DllImport("gobject-2.0.dll")]
                static extern int g_signal_handler_disconnect(IntPtr o, uint handler_id);
+
+               [DllImport("gobject-2.0.dll")]
+               static extern uint g_signal_lookup (string name, IntPtr itype);
+               
+               [DllImport("glib-2.0.dll")]
+               static extern uint g_quark_from_string (string str);
+
+               [DllImport("gobject-2.0.dll")]
+               static extern void g_signal_emitv (GLib.Value[] parameters, uint signal_id, uint detail, ref GLib.Value return_value);
+
+               [StructLayout(LayoutKind.Sequential)]
+               struct GSignalQuery {
+                       public uint signal_id;
+                       public IntPtr signal_name;
+                       public IntPtr itype;
+                       public uint signal_flags;
+                       public IntPtr return_type;
+                       public uint n_params;
+                       public IntPtr param_types;
+               }
+
+               [DllImport("gobject-2.0.dll")]
+               static extern void g_signal_query (uint signal_id, ref GSignalQuery query);
        }
 }
index d459c73..60cecc3 100644 (file)
@@ -5,7 +5,8 @@ libgstreamersharpglue_0_10_la_SOURCES = \
        clock.c \
        message.c \
        miniobject.c \
-       bin.c
+       bin.c \
+       dynamicsignal.c
 
 nodist_libgstreamersharpglue_0_10_la_SOURCES = generated.c 
 
diff --git a/gstreamer-sharp/glue/dynamicsignal.c b/gstreamer-sharp/glue/dynamicsignal.c
new file mode 100644 (file)
index 0000000..2a7d394
--- /dev/null
@@ -0,0 +1,13 @@
+#include <glib-object.h>
+
+gint
+gstsharp_g_closure_sizeof (void)
+{
+  return sizeof (GClosure);
+}
+
+GType
+gstsharp_g_type_from_instance (GTypeInstance *instance)
+{
+  return G_TYPE_FROM_INSTANCE (instance);
+}