From 10f98194467f0314df1aa6f850375b4c6b041ee5 Mon Sep 17 00:00:00 2001 From: lytico Date: Mon, 6 Apr 2020 21:55:17 +0200 Subject: [PATCH] GtkSharp.Container: implement https://github.com/mono/gtk-sharp/blob/master/gtk/glue/container.c --- Source/Libs/GtkSharp/Container.Forall.cs | 156 +++++++++++++++++++++++ Source/Libs/GtkSharp/Container.cs | 34 ----- 2 files changed, 156 insertions(+), 34 deletions(-) create mode 100644 Source/Libs/GtkSharp/Container.Forall.cs diff --git a/Source/Libs/GtkSharp/Container.Forall.cs b/Source/Libs/GtkSharp/Container.Forall.cs new file mode 100644 index 000000000..ba3fabe47 --- /dev/null +++ b/Source/Libs/GtkSharp/Container.Forall.cs @@ -0,0 +1,156 @@ +using GtkSharp; + +namespace Gtk +{ + using System; + using System.Runtime.InteropServices; + + public partial class Container + { + internal static D TryGetDelegate(IntPtr _cb) + { + try { + var d = Marshal.GetDelegateForFunctionPointer(_cb); + return d; + } catch { + return default; + } + } + + static ForAllNativeDelegate ForAll_cb_delegate; + + static ForAllNativeDelegate ForAllVMCallback { + get { + if (ForAll_cb_delegate == null) + ForAll_cb_delegate = new ForAllNativeDelegate(ForAll_cb); + return ForAll_cb_delegate; + } + } + + static void OverrideForAll(GLib.GType gtype) + { + OverrideForAll(gtype, ForAllVMCallback); + } + + /// + /// sets callback forall in GtkContainerClass-Struct + /// + /// + /// + static void OverrideForAll(GLib.GType gtype, ForAllNativeDelegate callback) + { + unsafe { + IntPtr* raw_ptr = (IntPtr*) (((long) gtype.GetClassPtr()) + (long) class_abi.GetFieldOffset("forall")); + *raw_ptr = Marshal.GetFunctionPointerForDelegate((Delegate) callback); + } + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void ForAllNativeDelegate(IntPtr inst, bool include_internals, IntPtr cb, IntPtr data); + + internal class ForAllCallbackHandler + { + internal IntPtr cb { get; } + internal IntPtr data { get; } + internal bool include_internals { get; } + + internal ForAllCallbackHandler(IntPtr cb, IntPtr data, bool include_internals) + { + this.cb = cb; + this.data = data; + this.include_internals = include_internals; + } + + internal void Invoke(Widget w) + { + var nativeCb = TryGetDelegate(cb); + var inv = new GtkSharp.CallbackInvoker(nativeCb, data); + inv.Handler?.Invoke(w); + } + } + + static void ForAll_cb(IntPtr inst, bool include_internals, IntPtr cb, IntPtr data) + { + try { + //GtkContainer's unmanaged dispose calls forall, but by that time the managed object is gone + //so it couldn't do anything useful, and resurrecting it would cause a resurrection cycle. + //In that case, just chain to the native base in case it can do something. + + var fcb = new ForAllCallbackHandler(cb, data, include_internals); + if (GLib.Object.TryGetObject(inst) is Container container) { + container.ForAll(include_internals, fcb.Invoke); + } else { + gtksharp_container_base_forall(inst, include_internals, fcb.Invoke, data); + } + } catch (Exception e) { + GLib.ExceptionManager.RaiseUnhandledException(e, false); + } + } + + [GLib.DefaultSignalHandler(Type = typeof(Gtk.Container), ConnectionMethod = nameof(OverrideForAll))] + protected virtual void ForAll(bool include_internals, Callback callback) + { + InternalForAll(include_internals, callback); + } + + private void InternalForAll(bool include_internals, Callback callback) + { + if (!(callback.Target is ForAllCallbackHandler)) { + throw new ApplicationException( + $"{nameof(ForAll)} can only be called as \"base.{nameof(ForAll)}()\". Use {nameof(Forall)}() or {nameof(Foreach)}()."); + } + + gtksharp_container_base_forall(Handle, include_internals, callback, IntPtr.Zero); + } + + static ForAllNativeDelegate InternalForAllNativeDelegate(GLib.GType gtype) + { + ForAllNativeDelegate unmanaged = null; + unsafe { + IntPtr* raw_ptr = (IntPtr*) (((long) gtype.GetClassPtr()) + (long) class_abi.GetFieldOffset("forall")); + if (*raw_ptr == IntPtr.Zero) + return default; + + unmanaged = Marshal.GetDelegateForFunctionPointer(*raw_ptr); + } + + return unmanaged; + } + + + static void gtksharp_container_base_forall(IntPtr container, bool include_internals, Callback cb, IntPtr data) + { + // gtksharp_container_base_forall from gtkglue: + // Find and call the first base callback that's not the GTK# callback. The GTK# callback calls down the whole + // managed override chain, so calling it on a subclass-of-a-managed-container-subclass causes a stack overflow. + + // GtkContainerClass *parent = (GtkContainerClass *) G_OBJECT_GET_CLASS (container); + // while ((parent = g_type_class_peek_parent (parent))) { + // if (strncmp (G_OBJECT_CLASS_NAME (parent), "__gtksharp_", 11) != 0) { + // if (parent->forall) { + // (*parent->forall) (container, include_internals, cb, data); + // } + // return; + // } + // } + if (container == IntPtr.Zero) + return; + + var obj = TryGetObject(container); + var parent = obj.NativeType; + while ((parent = parent.GetBaseType()) != GLib.GType.None) { + if (parent.ToString().StartsWith("__gtksharp_")) { + continue; + } + + var forAll = InternalForAllNativeDelegate(parent); + if (forAll != default && cb.Target is ForAllCallbackHandler cbh) { + GtkSharp.CallbackWrapper cb_wrapper = new GtkSharp.CallbackWrapper(cb); + forAll(container, include_internals, cbh.cb, data); + } + + return; + } + } + } +} \ No newline at end of file diff --git a/Source/Libs/GtkSharp/Container.cs b/Source/Libs/GtkSharp/Container.cs index b63fbc691..05a72b261 100644 --- a/Source/Libs/GtkSharp/Container.cs +++ b/Source/Libs/GtkSharp/Container.cs @@ -123,40 +123,6 @@ namespace Gtk } - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - delegate void ForallDelegate(IntPtr container, bool include_internals, IntPtr cb, IntPtr data); - - static ForallDelegate ForallOldCallback; - static ForallDelegate ForallCallback; - - public struct CallbackInvoker - { - IntPtr cb; - IntPtr data; - - internal CallbackInvoker(IntPtr cb, IntPtr data) - { - this.cb = cb; - this.data = data; - } - - internal IntPtr Data - { - get - { - return data; - } - } - - internal IntPtr Callback - { - get - { - return cb; - } - } - } - // Compatibility code for old ChildType() virtual method static IntPtr ObsoleteChildType_cb(IntPtr raw) {