diff --git a/ChangeLog b/ChangeLog index 2f102a786..265b73ece 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2009-09-11 Mike Kestner + + * glib/Object.cs: add support for native instantiation of + managed types. + * glib/ParamSpec.cs: add Flags ctor overload. + * sample/NativeInstantiationTest.cs: new sample to test the + unmanaged instantiation capability. + Based on a patch from Sebastian Dröge [Fixes #499900] + 2009-09-11 Christian Hoff * gtk/Application.cs: Port the theming-relevant part of the diff --git a/glib/Object.cs b/glib/Object.cs index 86368d608..3555b83e6 100644 --- a/glib/Object.cs +++ b/glib/Object.cs @@ -192,7 +192,7 @@ namespace GLib { struct GObjectClass { GTypeClass type_class; IntPtr construct_props; - IntPtr constructor_cb; + public ConstructorDelegate constructor_cb; public SetPropertyDelegate set_prop_cb; public GetPropertyDelegate get_prop_cb; IntPtr dispose; @@ -209,13 +209,49 @@ namespace GLib { IntPtr dummy7; } - static void OverridePropertyHandlers (GType gtype, GetPropertyDelegate get_cb, SetPropertyDelegate set_cb) + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + delegate IntPtr ConstructorDelegate (IntPtr gtype, uint n_construct_properties, IntPtr construct_properties); + + static ConstructorDelegate constructor_handler; + + static ConstructorDelegate ConstructorHandler { + get { + if (constructor_handler == null) + constructor_handler = new ConstructorDelegate (ConstructorCallback); + return constructor_handler; + } + } + + [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr g_param_spec_get_name (IntPtr pspec); + + static IntPtr ConstructorCallback (IntPtr gtypeval, uint n_construct_properties, IntPtr construct_properties) { - IntPtr class_ptr = gtype.GetClassPtr (); - GObjectClass klass = (GObjectClass) Marshal.PtrToStructure (class_ptr, typeof (GObjectClass)); - klass.get_prop_cb = get_cb; - klass.set_prop_cb = set_cb; - Marshal.StructureToPtr (klass, class_ptr, false); + GType gtype = new GLib.GType (gtypeval); + GObjectClass threshold_class = (GObjectClass) Marshal.PtrToStructure (gtype.GetThresholdType ().GetClassPtr (), typeof (GObjectClass)); + IntPtr raw = threshold_class.constructor_cb (gtypeval, n_construct_properties, construct_properties); + bool construct_needed = true; + for (int i = 0; i < n_construct_properties; i++) { + IntPtr p = new IntPtr (construct_properties.ToInt64 () + i * 2 * IntPtr.Size); + + string prop_name = Marshaller.Utf8PtrToString (g_param_spec_get_name (Marshal.ReadIntPtr (p))); + if (prop_name != "gtk-sharp-managed-instance") + continue; + + Value val = (Value) Marshal.PtrToStructure (Marshal.ReadIntPtr (p, IntPtr.Size), typeof (Value)); + if ((IntPtr) val.Val != IntPtr.Zero) { + GCHandle gch = (GCHandle) (IntPtr) val.Val; + Object o = (GLib.Object) gch.Target; + o.Raw = raw; + construct_needed = false; + break; + } + } + + if (construct_needed) + GetObject (raw, false); + + return raw; } [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] @@ -230,22 +266,32 @@ namespace GLib { return pspec.Handle; } - static void AddProperties (GType gtype, System.Type t) + static void AddProperties (GType gtype, System.Type t, bool register_instance_prop) { uint idx = 1; - bool handlers_overridden = false; + if (register_instance_prop) { + IntPtr declaring_class = gtype.GetClassPtr (); + ParamSpec pspec = new ParamSpec ("gtk-sharp-managed-instance", "", "", GType.Pointer, ParamFlags.Writable | ParamFlags.ConstructOnly); + g_object_class_install_property (declaring_class, idx, pspec.Handle); + idx++; + } + + bool handlers_overridden = register_instance_prop; foreach (PropertyInfo pinfo in t.GetProperties (BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)) { foreach (object attr in pinfo.GetCustomAttributes (typeof (PropertyAttribute), false)) { if(pinfo.GetIndexParameters().Length > 0) throw(new InvalidOperationException(String.Format("GLib.RegisterPropertyAttribute cannot be applied to property {0} of type {1} because the property expects one or more indexed parameters", pinfo.Name, t.FullName))); - PropertyAttribute property_attr = attr as PropertyAttribute; if (!handlers_overridden) { - OverridePropertyHandlers (gtype, GetPropertyHandler, SetPropertyHandler); + IntPtr class_ptr = gtype.GetClassPtr (); + GObjectClass gobject_class = (GObjectClass) Marshal.PtrToStructure (class_ptr, typeof (GObjectClass)); + gobject_class.get_prop_cb = GetPropertyHandler; + gobject_class.set_prop_cb = SetPropertyHandler; + Marshal.StructureToPtr (gobject_class, class_ptr, false); handlers_overridden = true; } - + PropertyAttribute property_attr = attr as PropertyAttribute; try { IntPtr param_spec = RegisterProperty (gtype, property_attr.Name, property_attr.Nickname, property_attr.Blurb, idx, (GType) pinfo.PropertyType, pinfo.CanRead, pinfo.CanWrite); Properties.Add (param_spec, pinfo); @@ -262,6 +308,9 @@ namespace GLib { static void GetPropertyCallback (IntPtr handle, uint property_id, ref GLib.Value value, IntPtr param_spec) { + if (!Properties.Contains (param_spec)) + return; + GLib.Object obj = GLib.Object.GetObject (handle, false); value.Val = (Properties [param_spec] as PropertyInfo).GetValue (obj, new object [0]); } @@ -280,6 +329,9 @@ namespace GLib { static void SetPropertyCallback(IntPtr handle, uint property_id, ref GLib.Value value, IntPtr param_spec) { + if (!Properties.Contains (param_spec)) + return; + GLib.Object obj = GLib.Object.GetObject (handle, false); (Properties [param_spec] as PropertyInfo).SetValue (obj, value.Val, new object [0]); } @@ -313,14 +365,22 @@ namespace GLib { protected internal static GType RegisterGType (System.Type t) { GType gtype = GType.RegisterGObjectType (t); - AddProperties (gtype, t); + bool is_first_subclass = gtype.GetBaseType () == gtype.GetThresholdType (); + if (is_first_subclass) { + IntPtr class_ptr = gtype.GetClassPtr (); + GObjectClass gobject_class = (GObjectClass) Marshal.PtrToStructure (class_ptr, typeof (GObjectClass)); + gobject_class.constructor_cb = ConstructorHandler; + gobject_class.get_prop_cb = GetPropertyHandler; + gobject_class.set_prop_cb = SetPropertyHandler; + Marshal.StructureToPtr (gobject_class, class_ptr, false); + } + AddProperties (gtype, t, is_first_subclass); ConnectDefaultHandlers (gtype, t); InvokeClassInitializers (gtype, t); AddInterfaces (gtype, t); return gtype; } - protected GType LookupGType () { if (Handle != IntPtr.Zero) { @@ -360,12 +420,24 @@ namespace GLib { protected virtual void CreateNativeObject (string[] names, GLib.Value[] vals) { - GParameter[] parms = new GParameter [names.Length]; + GType gtype = LookupGType (); + bool is_managed_subclass = gtype.ToString ().StartsWith ("__gtksharp"); + GParameter[] parms = new GParameter [is_managed_subclass ? names.Length + 1 : names.Length]; for (int i = 0; i < names.Length; i++) { parms [i].name = GLib.Marshaller.StringToPtrGStrdup (names [i]); parms [i].val = vals [i]; } - Raw = g_object_newv (LookupGType ().Val, parms.Length, parms); + + if (is_managed_subclass) { + GCHandle gch = GCHandle.Alloc (this); + parms[names.Length].name = GLib.Marshaller.StringToPtrGStrdup ("gtk-sharp-managed-instance"); + parms[names.Length].val = new GLib.Value ((IntPtr) gch); + Raw = g_object_newv (gtype.Val, parms.Length, parms); + gch.Free (); + } else { + Raw = g_object_newv (gtype.Val, parms.Length, parms); + } + foreach (GParameter p in parms) GLib.Marshaller.Free (p.name); } diff --git a/glib/ParamSpec.cs b/glib/ParamSpec.cs index 427aa501a..3e40a813a 100644 --- a/glib/ParamSpec.cs +++ b/glib/ParamSpec.cs @@ -28,17 +28,18 @@ namespace GLib { None = 0, Readable = 1 << 0, Writable = 1 << 1, + Construct = 1 << 2, + ConstructOnly = 1 << 3, } public class ParamSpec { IntPtr handle; - public ParamSpec (string name, string nick, string blurb, GType type, bool readable, bool writable) + public ParamSpec (string name, string nick, string blurb, GType type, bool readable, bool writable) : this (name, nick, blurb, type, (readable ? ParamFlags.Readable : ParamFlags.None) | (writable ? ParamFlags.Writable : ParamFlags.None)) {} + + internal ParamSpec (string name, string nick, string blurb, GType type, ParamFlags pflags) { - ParamFlags pflags = ParamFlags.None; - if (readable) pflags |= ParamFlags.Readable; - if (writable) pflags |= ParamFlags.Writable; int flags = (int) pflags; IntPtr p_name = GLib.Marshaller.StringToPtrGStrdup (name); diff --git a/sample/Makefile.am b/sample/Makefile.am index 0d9357502..48aea6820 100755 --- a/sample/Makefile.am +++ b/sample/Makefile.am @@ -22,7 +22,7 @@ DOTNET_TARGETS= DOTNET_ASSEMBLY= endif -TARGETS = polarfixed.exe custom-widget.exe custom-cellrenderer.exe gtk-hello-world.exe button.exe calendar.exe subclass.exe menu.exe size.exe scribble.exe scribble-xinput.exe treeviewdemo.exe managedtreeviewdemo.exe nodeviewdemo.exe treemodeldemo.exe testdnd.exe actions.exe spawn.exe assistant.exe registerprop.exe gexceptiontest.exe cairo-sample.exe $(GLADE_TARGETS) $(DOTNET_TARGETS) +TARGETS = polarfixed.exe custom-widget.exe custom-cellrenderer.exe gtk-hello-world.exe button.exe calendar.exe subclass.exe menu.exe size.exe scribble.exe scribble-xinput.exe treeviewdemo.exe managedtreeviewdemo.exe nodeviewdemo.exe treemodeldemo.exe testdnd.exe actions.exe spawn.exe assistant.exe registerprop.exe gexceptiontest.exe cairo-sample.exe native-instantiation.exe $(GLADE_TARGETS) $(DOTNET_TARGETS) DEBUGS = $(addsuffix .mdb, $(TARGETS)) @@ -55,6 +55,9 @@ subclass.exe: $(srcdir)/Subclass.cs $(assemblies) menu.exe: $(srcdir)/Menu.cs $(assemblies) $(CSC) /out:menu.exe $(references) $(srcdir)/Menu.cs +native-instantiation.exe: $(srcdir)/NativeInstantiationTest.cs $(assemblies) + $(CSC) /out:native-instantiation.exe $(references) $(srcdir)/NativeInstantiationTest.cs + size.exe: $(srcdir)/Size.cs $(assemblies) $(CSC) /out:size.exe $(references) $(srcdir)/Size.cs diff --git a/sample/NativeInstantiationTest.cs b/sample/NativeInstantiationTest.cs new file mode 100755 index 000000000..1e53e5da2 --- /dev/null +++ b/sample/NativeInstantiationTest.cs @@ -0,0 +1,56 @@ +// Author: Mike Kestner +// +// Copyright (c) 2009 Novell, Inc. + +namespace GtkSharp { + + using Gtk; + using System; + using System.Runtime.InteropServices; + + public class InstantiationTest : Gtk.Window { + + [DllImport ("libgobject-2.0.so.0")] + static extern IntPtr g_object_new (IntPtr gtype, string prop, string val, IntPtr dummy); + + [DllImport ("libgtk-x11-2.0.so.0")] + static extern void gtk_widget_show (IntPtr handle); + + public static int Main (string[] args) + { + Application.Init (); + GLib.GType gtype = LookupGType (typeof (InstantiationTest)); + GLib.GType.Register (gtype, typeof (InstantiationTest)); + Console.WriteLine ("Instantiating using managed constructor"); + new InstantiationTest ("Managed Instantiation Test").ShowAll (); + Console.WriteLine ("Managed Instantiation complete"); + Console.WriteLine ("Instantiating using unmanaged construction"); + IntPtr handle = g_object_new (gtype.Val, "title", "Unmanaged Instantiation Test", IntPtr.Zero); + gtk_widget_show (handle); + Console.WriteLine ("Unmanaged Instantiation complete"); + Application.Run (); + return 0; + } + + + public InstantiationTest (IntPtr raw) : base (raw) + { + Console.WriteLine ("IntPtr ctor invoked"); + DefaultWidth = 400; + DefaultHeight = 200; + } + + public InstantiationTest (string title) : base (title) + { + DefaultWidth = 200; + DefaultHeight = 400; + } + + protected override bool OnDeleteEvent (Gdk.Event ev) + { + Application.Quit (); + return true; + } + + } +}