From 0780ed3c0dd0692e4d1f95d7b7ab02d72972fdb4 Mon Sep 17 00:00:00 2001 From: Mike Kestner Date: Wed, 30 Apr 2008 20:15:45 +0000 Subject: [PATCH] 2008-04-30 Mike Kestner * generator/InterfaceGen.cs: * generator/Signal.cs: use generic signal marshaling instead of generating signature specific marshaling callbacks. * glib/glue/closure.c: glue for new closure impl. * glib/Object.cs: open up a couple hashes internally. * glib/Signal.cs: hook in closure based connection and expose EmissionHook functionality for atk usage. * glib/SignalClosure.cs: new generic signal marshaling mechanism. * glib/ToggleRef.cs: null guarding in Target and let Signal remove itself from hash when it disposes itself. svn path=/trunk/gtk-sharp/; revision=102241 --- ChangeLog | 13 ++ generator/InterfaceGen.cs | 4 +- generator/Signal.cs | 143 ++++-------------- glib/Makefile.am | 1 + glib/Object.cs | 11 +- glib/Signal.cs | 310 ++++++++++++++++++++++++-------------- glib/SignalClosure.cs | 188 +++++++++++++++++++++++ glib/ToggleRef.cs | 13 +- glib/glue/Makefile.am | 1 + glib/glue/closure.c | 35 +++++ 10 files changed, 480 insertions(+), 239 deletions(-) create mode 100644 glib/SignalClosure.cs create mode 100644 glib/glue/closure.c diff --git a/ChangeLog b/ChangeLog index 42c5a0089..441dfcfc0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2008-04-30 Mike Kestner + + * generator/InterfaceGen.cs: + * generator/Signal.cs: use generic signal marshaling instead of + generating signature specific marshaling callbacks. + * glib/glue/closure.c: glue for new closure impl. + * glib/Object.cs: open up a couple hashes internally. + * glib/Signal.cs: hook in closure based connection and expose + EmissionHook functionality for atk usage. + * glib/SignalClosure.cs: new generic signal marshaling mechanism. + * glib/ToggleRef.cs: null guarding in Target and let Signal remove + itself from hash when it disposes itself. + 2008-04-30 Mike Kestner * parser/gapi2xml.pl: put class struct field in the signal elems. diff --git a/generator/InterfaceGen.cs b/generator/InterfaceGen.cs index e63fcd50e..c5f9dea29 100644 --- a/generator/InterfaceGen.cs +++ b/generator/InterfaceGen.cs @@ -259,10 +259,8 @@ namespace GtkSharp.Generation { GenProperties (gen_info, null); - foreach (Signal sig in sigs.Values) { - sig.GenCallback (sw); + foreach (Signal sig in sigs.Values) sig.GenEvent (sw, null, "GLib.Object.GetObject (Handle)"); - } Method temp = methods ["GetType"] as Method; if (temp != null) diff --git a/generator/Signal.cs b/generator/Signal.cs index 0eb4b8d87..be4b2f8eb 100644 --- a/generator/Signal.cs +++ b/generator/Signal.cs @@ -85,35 +85,6 @@ namespace GtkSharp.Generation { } } - public string CallbackName { - get { - return Name + "SignalCallback"; - } - } - - private string CallbackSig { - get { - string result = ""; - for (int i = 0; i < parms.Count; i++) { - if (i > 0) - result += ", "; - - Parameter p = parms [i]; - if (p.PassAs != "" && !(p.Generatable is StructBase)) - result += p.PassAs + " "; - result += (p.MarshalType + " arg" + i); - } - - return result; - } - } - - public string DelegateName { - get { - return Name + "SignalDelegate"; - } - } - private string EventArgsName { get { if (IsEventHandler) @@ -168,7 +139,7 @@ namespace GtkSharp.Generation { } } - public bool IsEventHandler { + private bool IsEventHandler { get { return retval.CSType == "void" && parms.Count == 1 && (parms [0].Generatable is ObjectGen || parms [0].Generatable is InterfaceGen); } @@ -204,82 +175,6 @@ namespace GtkSharp.Generation { } } - public string GenArgsInitialization (StreamWriter sw) - { - if (parms.Count > 1) - sw.WriteLine("\t\t\t\targs.Args = new object[" + (parms.Count - 1) + "];"); - string finish = ""; - for (int idx = 1; idx < parms.Count; idx++) { - Parameter p = parms [idx]; - IGeneratable igen = p.Generatable; - if (p.PassAs != "out") { - if (igen is ManualGen) { - sw.WriteLine("\t\t\t\tif (arg{0} == IntPtr.Zero)", idx); - sw.WriteLine("\t\t\t\t\targs.Args[{0}] = null;", idx - 1); - sw.WriteLine("\t\t\t\telse {"); - sw.WriteLine("\t\t\t\t\targs.Args[" + (idx - 1) + "] = " + p.FromNative ("arg" + idx) + ";"); - sw.WriteLine("\t\t\t\t}"); - } else - sw.WriteLine("\t\t\t\targs.Args[" + (idx - 1) + "] = " + p.FromNative ("arg" + idx) + ";"); - } - if (igen is StructBase && p.PassAs == "ref") - finish += "\t\t\t\tif (arg" + idx + " != IntPtr.Zero) System.Runtime.InteropServices.Marshal.StructureToPtr (args.Args[" + (idx-1) + "], arg" + idx + ", false);\n"; - else if (p.PassAs != "") - finish += "\t\t\t\targ" + idx + " = " + igen.ToNativeReturn ("((" + p.CSType + ")args.Args[" + (idx - 1) + "])") + ";\n"; - } - return finish; - } - - public void GenArgsCleanup (StreamWriter sw, string finish) - { - if (IsVoid && finish.Length == 0) - return; - - sw.WriteLine("\n\t\t\ttry {"); - sw.Write (finish); - if (!IsVoid) { - if (retval.CSType == "bool") { - sw.WriteLine ("\t\t\t\tif (args.RetVal == null)"); - sw.WriteLine ("\t\t\t\t\treturn false;"); - } - sw.WriteLine("\t\t\t\treturn " + SymbolTable.Table.ToNativeReturn (retval.CType, "((" + retval.CSType + ")args.RetVal)") + ";"); - } - sw.WriteLine("\t\t\t} catch (Exception) {"); - sw.WriteLine ("\t\t\t\tException ex = new Exception (\"args.RetVal or 'out' property unset or set to incorrect type in " + EventHandlerQualifiedName + " callback\");"); - sw.WriteLine("\t\t\t\tGLib.ExceptionManager.RaiseUnhandledException (ex, true);"); - - sw.WriteLine ("\t\t\t\t// NOTREACHED: above call doesn't return."); - sw.WriteLine ("\t\t\t\tthrow ex;"); - sw.WriteLine("\t\t\t}"); - } - - public void GenCallback (StreamWriter sw) - { - if (IsEventHandler) - return; - - sw.WriteLine ("\t\t[GLib.CDeclCallback]"); - sw.WriteLine ("\t\tdelegate " + retval.ToNativeType + " " + DelegateName + " (" + CallbackSig + ", IntPtr gch);"); - sw.WriteLine (); - sw.WriteLine ("\t\tstatic " + retval.ToNativeType + " " + CallbackName + " (" + CallbackSig + ", IntPtr gch)"); - sw.WriteLine("\t\t{"); - sw.WriteLine("\t\t\t{0} args = new {0} ();", EventArgsQualifiedName); - sw.WriteLine("\t\t\ttry {"); - sw.WriteLine("\t\t\t\tGLib.Signal sig = ((GCHandle) gch).Target as GLib.Signal;"); - sw.WriteLine("\t\t\t\tif (sig == null)"); - sw.WriteLine("\t\t\t\t\tthrow new Exception(\"Unknown signal GC handle received \" + gch);"); - sw.WriteLine(); - string finish = GenArgsInitialization (sw); - sw.WriteLine("\t\t\t\t{0} handler = ({0}) sig.Handler;", EventHandlerQualifiedName); - sw.WriteLine("\t\t\t\thandler (GLib.Object.GetObject (arg0), args);"); - sw.WriteLine("\t\t\t} catch (Exception e) {"); - sw.WriteLine("\t\t\t\tGLib.ExceptionManager.RaiseUnhandledException (e, false);"); - sw.WriteLine("\t\t\t}"); - GenArgsCleanup (sw, finish); - sw.WriteLine("\t\t}"); - sw.WriteLine(); - } - private bool NeedNew (ClassBase implementor) { return elem.HasAttribute ("new_flag") || @@ -384,11 +279,31 @@ namespace GtkSharp.Generation { sw.WriteLine ("\t\t}\n"); } - private void GenDefaultHandlerDelegate (StreamWriter sw, ClassBase implementor) + private void GenDefaultHandlerDelegate (GenerationInfo gen_info, ClassBase implementor) { + StreamWriter sw = gen_info.Writer; + StreamWriter glue = gen_info.GlueWriter; + bool use_glue = false; + //bool use_glue = glue != null && implementor == null; + string glue_name = String.Empty; ManagedCallString call = new ManagedCallString (parms); sw.WriteLine ("\t\t[GLib.CDeclCallback]"); sw.WriteLine ("\t\tdelegate " + retval.ToNativeType + " " + Name + "VMDelegate (" + parms.ImportSignature + ");\n"); + + if (use_glue) { + glue_name = String.Format ("{0}sharp_{1}_{2}", container_type.NS.ToLower ().Replace (".", "_"), container_type.Name.ToLower (), ClassFieldName); + sw.WriteLine ("\t\t[DllImport (\"{0}\")]", gen_info.GluelibName); + sw.WriteLine ("\t\tstatic extern void {0} (IntPtr gtype, {1}VMDelegate cb);\n", glue_name, Name); + glue.WriteLine ("void {0} (GType gtype, gpointer cb);\n", glue_name); + glue.WriteLine ("void\n{0} (GType gtype, gpointer cb)", glue_name); + glue.WriteLine ("{"); + glue.WriteLine ("\tGObjectClass *klass = g_type_class_peek (gtype);"); + glue.WriteLine ("\tif (klass == NULL)"); + glue.WriteLine ("\t\tklass = g_type_class_ref (gtype);"); + glue.WriteLine ("\t(({0} *)klass)->{1} = cb;", container_type.CName + "Class", ClassFieldName); + glue.WriteLine ("}\n"); + } + sw.WriteLine ("\t\tstatic {0} {1};\n", Name + "VMDelegate", Name + "VMCallback"); sw.WriteLine ("\t\tstatic " + retval.ToNativeType + " " + Name.ToLower() + "_cb (" + parms.ImportSignature + ")"); sw.WriteLine ("\t\t{"); @@ -413,13 +328,16 @@ namespace GtkSharp.Generation { sw.WriteLine ("\t\t{"); sw.WriteLine ("\t\t\tif (" + Name + "VMCallback == null)"); sw.WriteLine ("\t\t\t\t" + Name + "VMCallback = new " + Name + "VMDelegate (" + Name.ToLower() + "_cb);"); - sw.WriteLine ("\t\t\tOverrideVirtualMethod (gtype, " + CName + ", " + Name + "VMCallback);"); + if (use_glue) + sw.WriteLine ("\t\t\t{0} (gtype.Val, {1}VMCallback);", glue_name, Name); + else + sw.WriteLine ("\t\t\tOverrideVirtualMethod (gtype, " + CName + ", " + Name + "VMCallback);"); sw.WriteLine ("\t\t}\n"); } public void GenEvent (StreamWriter sw, ClassBase implementor, string target) { - string marsh = IsEventHandler ? "" : ", new " + DelegateName + "(" + CallbackName + ")"; + string args_type = IsEventHandler ? "" : ", typeof (" + EventArgsQualifiedName + ")"; sw.WriteLine("\t\t[GLib.Signal("+ CName + ")]"); sw.Write("\t\tpublic "); @@ -427,11 +345,11 @@ namespace GtkSharp.Generation { sw.Write("new "); sw.WriteLine("event " + EventHandlerQualifiedName + " " + Name + " {"); sw.WriteLine("\t\t\tadd {"); - sw.WriteLine("\t\t\t\tGLib.Signal sig = GLib.Signal.Lookup (" + target + ", " + CName + marsh + ");"); + sw.WriteLine("\t\t\t\tGLib.Signal sig = GLib.Signal.Lookup (" + target + ", " + CName + args_type + ");"); sw.WriteLine("\t\t\t\tsig.AddDelegate (value);"); sw.WriteLine("\t\t\t}"); sw.WriteLine("\t\t\tremove {"); - sw.WriteLine("\t\t\t\tGLib.Signal sig = GLib.Signal.Lookup (" + target + ", " + CName + marsh + ");"); + sw.WriteLine("\t\t\t\tGLib.Signal sig = GLib.Signal.Lookup (" + target + ", " + CName + args_type + ");"); sw.WriteLine("\t\t\t\tsig.RemoveDelegate (value);"); sw.WriteLine("\t\t\t}"); sw.WriteLine("\t\t}"); @@ -445,8 +363,7 @@ namespace GtkSharp.Generation { if (implementor == null) GenEventHandler (gen_info); - GenCallback (sw); - GenDefaultHandlerDelegate (sw, implementor); + GenDefaultHandlerDelegate (gen_info, implementor); GenVirtualMethod (sw, implementor); GenEvent (sw, implementor, "this"); diff --git a/glib/Makefile.am b/glib/Makefile.am index a0105ddfc..a40a70e85 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -59,6 +59,7 @@ sources = \ SignalArgs.cs \ SignalAttribute.cs \ SignalCallback.cs \ + SignalClosure.cs \ SList.cs \ Source.cs \ Spawn.cs \ diff --git a/glib/Object.cs b/glib/Object.cs index d76214fce..2c0127fbb 100644 --- a/glib/Object.cs +++ b/glib/Object.cs @@ -350,7 +350,7 @@ namespace GLib { Hashtable before_signals; [Obsolete ("Replaced by GLib.Signal marshaling mechanism.")] - protected Hashtable BeforeSignals { + protected internal Hashtable BeforeSignals { get { if (before_signals == null) before_signals = new Hashtable (); @@ -360,7 +360,7 @@ namespace GLib { Hashtable after_signals; [Obsolete ("Replaced by GLib.Signal marshaling mechanism.")] - protected Hashtable AfterSignals { + protected internal Hashtable AfterSignals { get { if (after_signals == null) after_signals = new Hashtable (); @@ -520,5 +520,12 @@ namespace GLib { { tref.Harden (); } + +#if false + static Object () + { + GLib.Log.SetLogHandler ("GLib-GObject", GLib.LogLevelFlags.All, GLib.Log.PrintTraceLogFunction); + } +#endif } } diff --git a/glib/Signal.cs b/glib/Signal.cs index 6c23422ec..d1b3a3fa8 100644 --- a/glib/Signal.cs +++ b/glib/Signal.cs @@ -31,61 +31,182 @@ namespace GLib { Swapped = 1 << 1, } - [Flags] - internal enum SignalFlags { - RunFirst = 1 << 0, - RunLast = 1 << 1, - RunCleanup = 1 << 2, - NoRecurse = 1 << 3, - Detailed = 1 << 4, - Action = 1 << 5, - NoHooks = 1 << 6 - } - - [StructLayout (LayoutKind.Sequential)] - internal struct InvocationHint { - public uint signal_id; - public uint detail; - public SignalFlags run_type; - } - public class Signal { - GCHandle gc_handle; + [Flags] + public enum Flags { + RunFirst = 1 << 0, + RunLast = 1 << 1, + RunCleanup = 1 << 2, + NoRecurse = 1 << 3, + Detailed = 1 << 4, + Action = 1 << 5, + NoHooks = 1 << 6 + } + + [StructLayout (LayoutKind.Sequential)] + public struct InvocationHint { + public uint signal_id; + public uint detail; + public Flags run_type; + } + + [CDeclCallback] + public delegate bool EmissionHookNative (ref InvocationHint hint, uint n_pvals, IntPtr pvals, IntPtr data); + + public delegate bool EmissionHook (InvocationHint ihint, object[] inst_and_param_values); + + public class EmissionHookMarshaler { + + EmissionHook handler; + EmissionHookNative cb; + IntPtr user_data; + GCHandle gch; + + public EmissionHookMarshaler (EmissionHook handler) + { + this.handler = handler; + cb = new EmissionHookNative (NativeCallback); + gch = GCHandle.Alloc (this); + } + + public EmissionHookMarshaler (EmissionHookNative callback, IntPtr user_data) + { + cb = callback; + this.user_data = user_data; + handler = new EmissionHook (NativeInvoker); + } + + bool NativeCallback (ref InvocationHint hint, uint n_pvals, IntPtr pvals_ptr, IntPtr data) + { + object[] pvals = new object [n_pvals]; + for (int i = 0; i < n_pvals; i++) { + IntPtr p = new IntPtr ((long) pvals_ptr + i * Marshal.SizeOf (typeof (Value))); + Value v = (Value) Marshal.PtrToStructure (p, typeof (Value)); + pvals [i] = v.Val; + } + bool result = handler (hint, pvals); + if (!result) + gch.Free (); + return result; + } + + public EmissionHookNative Callback { + get { + return cb; + } + } + + bool NativeInvoker (InvocationHint ihint, object[] pvals) + { + int val_sz = Marshal.SizeOf (typeof (Value)); + IntPtr buf = Marshal.AllocHGlobal (pvals.Length * val_sz); + Value[] vals = new Value [pvals.Length]; + for (int i = 0; i < pvals.Length; i++) { + vals [i] = new Value (pvals [i]); + IntPtr p = new IntPtr ((long) buf + i * val_sz); + Marshal.StructureToPtr (vals [i], p, false); + } + bool result = cb (ref ihint, (uint) pvals.Length, buf, user_data); + foreach (Value v in vals) + v.Dispose (); + Marshal.FreeHGlobal (buf); + return result; + } + + public EmissionHook Invoker { + get { + return handler; + } + } + } + ToggleRef tref; string name; - uint before_id = UInt32.MaxValue; - uint after_id = UInt32.MaxValue; - Delegate marshaler; - - ~Signal () - { - gc_handle.Free (); - } + Type args_type; + SignalClosure before_closure; + SignalClosure after_closure; private Signal (GLib.Object obj, string signal_name, Delegate marshaler) { tref = obj.ToggleRef; name = signal_name; - this.marshaler = marshaler; - gc_handle = GCHandle.Alloc (this, GCHandleType.Weak); + tref.Signals [name] = this; + } + + private Signal (GLib.Object obj, string signal_name, Type args_type) + { + tref = obj.ToggleRef; + name = signal_name; + this.args_type = args_type; tref.Signals [name] = this; } internal void Free () { - DisconnectHandler (before_id); - DisconnectHandler (after_id); - before_handler = after_handler = marshaler = null; - gc_handle.Free (); + if (before_closure != null) + before_closure.Dispose (); + if (after_closure != null) + after_closure.Dispose (); GC.SuppressFinalize (this); } + void ClosureDisposedCB (object o, EventArgs args) + { + if (o == before_closure) { + before_closure.Disposed -= ClosureDisposedHandler; + before_closure.Invoked -= ClosureInvokedCB; + if (tref.Target != null) + tref.Target.BeforeSignals.Remove (name); + before_closure = null; + } else { + after_closure.Disposed -= ClosureDisposedHandler; + after_closure.Invoked -= ClosureInvokedCB; + if (tref.Target != null) + tref.Target.AfterSignals.Remove (name); + after_closure = null; + } + + if (before_closure == null && after_closure == null) + tref.Signals.Remove (name); + } + + EventHandler closure_disposed_cb; + EventHandler ClosureDisposedHandler { + get { + if (closure_disposed_cb == null) + closure_disposed_cb = new EventHandler (ClosureDisposedCB); + return closure_disposed_cb; + } + } + + void ClosureInvokedCB (object o, ClosureInvokedArgs args) + { + Delegate handler; + if (o == before_closure) + handler = args.Target.BeforeSignals [name] as Delegate; + else + handler = args.Target.AfterSignals [name] as Delegate; + + if (handler != null) + handler.DynamicInvoke (new object[] {args.Target, args.Args}); + } + + ClosureInvokedHandler closure_invoked_cb; + ClosureInvokedHandler ClosureInvokedHandler { + get { + if (closure_invoked_cb == null) + closure_invoked_cb = new ClosureInvokedHandler (ClosureInvokedCB); + return closure_invoked_cb; + } + } + public static Signal Lookup (GLib.Object obj, string name) { - return Lookup (obj, name, EventHandlerDelegate); + return Lookup (obj, name, typeof (EventArgs)); } + [Obsolete ("Replaced by Lookup (Object obj, string name, Type args_type)")] public static Signal Lookup (GLib.Object obj, string name, Delegate marshaler) { Signal result = obj.ToggleRef.Signals [name] as Signal; @@ -94,103 +215,69 @@ namespace GLib { return result; } - Delegate before_handler; - Delegate after_handler; + public static Signal Lookup (GLib.Object obj, string name, Type args_type) + { + Signal result = obj.ToggleRef.Signals [name] as Signal; + if (result == null) + result = new Signal (obj, name, args_type); + return result; + } + public Delegate Handler { get { InvocationHint hint = (InvocationHint) Marshal.PtrToStructure (g_signal_get_invocation_hint (tref.Handle), typeof (InvocationHint)); - if (hint.run_type == SignalFlags.RunFirst) - return before_handler; + if (hint.run_type == Flags.RunFirst) + return tref.Target.BeforeSignals [name] as Delegate; else - return after_handler; + return tref.Target.AfterSignals [name] as Delegate; } } - uint Connect (int flags) - { - IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name); - uint id = g_signal_connect_data (tref.Handle, native_name, marshaler, (IntPtr) gc_handle, IntPtr.Zero, flags); - GLib.Marshaller.Free (native_name); - return id; - } - public void AddDelegate (Delegate d) { + if (args_type == null) + args_type = d.Method.GetParameters ()[1].ParameterType; + if (d.Method.IsDefined (typeof (ConnectBeforeAttribute), false)) { - if (before_handler == null) { - before_handler = d; - before_id = Connect (0); - } else - before_handler = Delegate.Combine (before_handler, d); + tref.Target.BeforeSignals [name] = Delegate.Combine (tref.Target.BeforeSignals [name] as Delegate, d); + if (before_closure == null) { + before_closure = new SignalClosure (tref.Handle, name, args_type); + before_closure.Disposed += ClosureDisposedHandler; + before_closure.Invoked += ClosureInvokedHandler; + before_closure.Connect (false); + } } else { - if (after_handler == null) { - after_handler = d; - after_id = Connect (1); - } else - after_handler = Delegate.Combine (after_handler, d); + tref.Target.AfterSignals [name] = Delegate.Combine (tref.Target.AfterSignals [name] as Delegate, d); + if (after_closure == null) { + after_closure = new SignalClosure (tref.Handle, name, args_type); + after_closure.Disposed += ClosureDisposedHandler; + after_closure.Invoked += ClosureInvokedHandler; + after_closure.Connect (true); + } } } public void RemoveDelegate (Delegate d) { + if (tref.Target == null) + return; + if (d.Method.IsDefined (typeof (ConnectBeforeAttribute), false)) { - before_handler = Delegate.Remove (before_handler, d); - if (before_handler == null) { - DisconnectHandler (before_id); - before_id = UInt32.MaxValue; + tref.Target.BeforeSignals [name] = Delegate.Remove (tref.Target.BeforeSignals [name] as Delegate, d); + if (tref.Target.BeforeSignals [name] == null && before_closure != null) { + before_closure.Dispose (); + before_closure = null; } } else { - after_handler = Delegate.Remove (after_handler, d); - if (after_handler == null) { - DisconnectHandler (after_id); - after_id = UInt32.MaxValue; + tref.Target.AfterSignals [name] = Delegate.Remove (tref.Target.AfterSignals [name] as Delegate, d); + if (tref.Target.AfterSignals [name] == null && after_closure != null) { + after_closure.Dispose (); + after_closure = null; } } - - if (after_id == UInt32.MaxValue && before_id == UInt32.MaxValue) - tref.Signals.Remove (name); } - void DisconnectHandler (uint handler_id) - { - if (handler_id != UInt32.MaxValue && g_signal_handler_is_connected (tref.Handle, handler_id)) - g_signal_handler_disconnect (tref.Handle, handler_id); - } - - [CDeclCallback] - delegate void voidObjectDelegate (IntPtr handle, IntPtr gch); - - static void voidObjectCallback (IntPtr handle, IntPtr data) - { - try { - if (data == IntPtr.Zero) - return; - GCHandle gch = (GCHandle) data; - if (gch.Target == null) - return; - Signal sig = gch.Target as Signal; - if (sig == null) { - ExceptionManager.RaiseUnhandledException (new Exception ("Unknown signal class GC handle received."), false); - return; - } - - EventHandler handler = (EventHandler) sig.Handler; - handler (Object.GetObject (handle), EventArgs.Empty); - } catch (Exception e) { - ExceptionManager.RaiseUnhandledException (e, false); - } - } - - static voidObjectDelegate event_handler_delegate; - static voidObjectDelegate EventHandlerDelegate { - get { - if (event_handler_delegate == null) - event_handler_delegate = new voidObjectDelegate (voidObjectCallback); - return event_handler_delegate; - } - } - public static object Emit (GLib.Object instance, string signal_name, string detail, params object[] args) { uint signal_id = GetSignalId (signal_name, instance); @@ -233,18 +320,9 @@ namespace GLib { return signal_id; } - [DllImport("libgobject-2.0-0.dll")] - static extern uint g_signal_connect_data(IntPtr obj, IntPtr name, Delegate cb, IntPtr gc_handle, IntPtr dummy, int flags); - [DllImport("libgobject-2.0-0.dll")] static extern IntPtr g_signal_get_invocation_hint (IntPtr instance); - [DllImport("libgobject-2.0-0.dll")] - static extern void g_signal_handler_disconnect (IntPtr instance, uint handler); - - [DllImport("libgobject-2.0-0.dll")] - static extern bool g_signal_handler_is_connected (IntPtr instance, uint handler); - [DllImport("libgobject-2.0-0.dll")] static extern void g_signal_emitv (IntPtr instance_and_params, uint signal_id, uint gquark_detail, ref GLib.Value return_value); diff --git a/glib/SignalClosure.cs b/glib/SignalClosure.cs new file mode 100644 index 000000000..138e6c0be --- /dev/null +++ b/glib/SignalClosure.cs @@ -0,0 +1,188 @@ +// SignalClosure.cs - signal marshaling class +// +// Authors: Mike Kestner +// +// Copyright (c) 2008 Novell, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the Lesser GNU General +// Public License as published by the Free Software Foundation. +// +// This program 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 Public +// License along with this program; if not, write to the +// Free Software Foundation, Inc., 59 Temple Place - Suite 330, +// Boston, MA 02111-1307, USA. + + +namespace GLib { + + using System; + using System.Collections; + using System.Runtime.InteropServices; + + internal class ClosureInvokedArgs : EventArgs { + + EventArgs args; + GLib.Object obj; + object result; + + public ClosureInvokedArgs (GLib.Object obj, EventArgs args) + { + this.obj = obj; + this.args = args; + } + + public EventArgs Args { + get { + return args; + } + } + + public GLib.Object Target { + get { + return obj; + } + } + } + + internal delegate void ClosureInvokedHandler (object o, ClosureInvokedArgs args); + + internal class SignalClosure : IDisposable { + + IntPtr handle; + IntPtr raw_closure; + string name; + uint id = UInt32.MaxValue; + System.Type args_type; + + static Hashtable closures = new Hashtable (); + + public SignalClosure (IntPtr obj, string signal_name, System.Type args_type) + { + raw_closure = glibsharp_closure_new (Marshaler, Notify, IntPtr.Zero); + closures [raw_closure] = this; + handle = obj; + name = signal_name; + this.args_type = args_type; + } + + public event EventHandler Disposed; + public event ClosureInvokedHandler Invoked; + + public void Connect (bool is_after) + { + IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name); + id = g_signal_connect_closure (handle, native_name, raw_closure, is_after); + GLib.Marshaller.Free (native_name); + } + + public void Disconnect () + { + if (id != UInt32.MaxValue && g_signal_handler_is_connected (handle, id)) + g_signal_handler_disconnect (handle, id); + } + + public void Dispose () + { + Disconnect (); + closures.Remove (raw_closure); + if (Disposed != null) + Disposed (this, EventArgs.Empty); + GC.SuppressFinalize (this); + } + + public void Invoke (ClosureInvokedArgs args) + { + if (Invoked == null) + return; + Invoked (this, args); + } + + static ClosureMarshal marshaler; + static ClosureMarshal Marshaler { + get { + if (marshaler == null) + marshaler = new ClosureMarshal (MarshalCallback); + return marshaler; + } + } + + [CDeclCallback] + delegate void ClosureMarshal (IntPtr closure, IntPtr return_val, uint n_param_vals, IntPtr param_values, IntPtr invocation_hint, IntPtr marshal_data); + + static void MarshalCallback (IntPtr raw_closure, IntPtr return_val, uint n_param_vals, IntPtr param_values, IntPtr invocation_hint, IntPtr marshal_data) + { + string message = String.Empty; + + try { + SignalClosure closure = closures [raw_closure] as SignalClosure; + message = "Marshaling " + closure.name + " signal"; + Value objval = (Value) Marshal.PtrToStructure (param_values, typeof (Value)); + GLib.Object __obj = objval.Val as GLib.Object; + if (__obj == null) + return; + + if (closure.args_type == typeof (EventArgs)) { + closure.Invoke (new ClosureInvokedArgs (__obj, EventArgs.Empty)); + return; + } + + SignalArgs args = Activator.CreateInstance (closure.args_type, new object [0]) as SignalArgs; + args.Args = new object [n_param_vals - 1]; + for (int i = 1; i < n_param_vals; i++) { + IntPtr ptr = new IntPtr ((long)param_values + i * Marshal.SizeOf (typeof (Value))); + Value val = (Value) Marshal.PtrToStructure (ptr, typeof (Value)); + args.Args [i - 1] = val.Val; + } + ClosureInvokedArgs ci_args = new ClosureInvokedArgs (__obj, args); + closure.Invoke (ci_args); + if (return_val == IntPtr.Zero || args.RetVal == null) + return; + + Value ret = (Value) Marshal.PtrToStructure (return_val, typeof (Value)); + ret.Val = args.RetVal; + Marshal.StructureToPtr (ret, return_val, false); + } catch (Exception e) { + Console.WriteLine (message); + ExceptionManager.RaiseUnhandledException (e, false); + } + } + + [CDeclCallback] + delegate void ClosureNotify (IntPtr data, IntPtr closure); + + static void NotifyCallback (IntPtr data, IntPtr raw_closure) + { + SignalClosure closure = closures [raw_closure] as SignalClosure; + if (closure != null) + closure.Dispose (); + } + + static ClosureNotify notify_handler; + static ClosureNotify Notify { + get { + if (notify_handler == null) + notify_handler = new ClosureNotify (NotifyCallback); + return notify_handler; + } + } + + [DllImport("glibsharpglue-2")] + static extern IntPtr glibsharp_closure_new (ClosureMarshal marshaler, ClosureNotify notify, IntPtr gch); + + [DllImport("libgobject-2.0-0.dll")] + static extern uint g_signal_connect_closure (IntPtr obj, IntPtr name, IntPtr closure, bool is_after); + + [DllImport("libgobject-2.0-0.dll")] + static extern void g_signal_handler_disconnect (IntPtr instance, uint handler); + + [DllImport("libgobject-2.0-0.dll")] + static extern bool g_signal_handler_is_connected (IntPtr instance, uint handler); + } +} + diff --git a/glib/ToggleRef.cs b/glib/ToggleRef.cs index 73f2534fe..a0235f495 100644 --- a/glib/ToggleRef.cs +++ b/glib/ToggleRef.cs @@ -69,7 +69,9 @@ namespace GLib { public GLib.Object Target { get { - if (reference is GLib.Object) + if (reference == null) + return null; + else if (reference is GLib.Object) return reference as GLib.Object; WeakReference weak = reference as WeakReference; @@ -77,11 +79,12 @@ namespace GLib { } } - public void Free () - { - foreach (Signal s in Signals.Values) + public void Free () + { + Signal[] signals = new Signal [Signals.Count]; + Signals.Values.CopyTo (signals, 0); + foreach (Signal s in signals) s.Free (); - Signals.Clear (); if (hardened) g_object_unref (handle); else diff --git a/glib/glue/Makefile.am b/glib/glue/Makefile.am index b95ae7a71..203eef951 100644 --- a/glib/glue/Makefile.am +++ b/glib/glue/Makefile.am @@ -3,6 +3,7 @@ lib_LTLIBRARIES = libglibsharpglue-2.la libglibsharpglue_2_la_LDFLAGS = -module -avoid-version -no-undefined libglibsharpglue_2_la_SOURCES = \ + closure.c \ error.c \ list.c \ object.c \ diff --git a/glib/glue/closure.c b/glib/glue/closure.c new file mode 100644 index 000000000..21d51c1e9 --- /dev/null +++ b/glib/glue/closure.c @@ -0,0 +1,35 @@ +/* closure.c : Native closure implementation + * + * Author: Mike Kestner + * + * Copyright (c) 2008 Novell, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the Lesser GNU General + * Public License as published by the Free Software Foundation. + * + * This program 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 Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +/* Forward declarations */ +GClosure* glibsharp_closure_new (GClosureMarshal marshaler, GClosureNotify notify, gpointer data); +/* */ + +GClosure* +glibsharp_closure_new (GClosureMarshal marshaler, GClosureNotify notify, gpointer data) +{ + GClosure *closure = g_closure_new_simple (sizeof (GClosure), data); + g_closure_set_marshal (closure, marshaler); + g_closure_add_finalize_notifier (closure, data, notify); + return closure; +}