Ryujinx-GtkSharp/Source/Libs/GtkSharp/Container.Forall.cs
Andrii Kurdiumov 723fef9d8d
Use Marshal.GetFunctionPointerForDelegate<T> (#301)
This make code more trim friendly

Improve #300
2022-01-25 19:46:06 +01:00

160 lines
4.9 KiB
C#

using GtkSharp;
namespace Gtk
{
using System;
using System.Runtime.InteropServices;
public partial class Container
{
internal static D TryGetDelegate<D>(IntPtr _cb)
{
try {
var d = Marshal.GetDelegateForFunctionPointer<D>(_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);
}
/// <summary>
/// sets callback forall in GtkContainerClass-Struct
/// </summary>
/// <param name="gtype"></param>
/// <param name="callback"></param>
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(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<CallbackNative>(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, ((ForAllCallbackHandler) callback.Target).data);
}
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<ForAllNativeDelegate>(*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);
if (obj == default)
return;
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;
}
}
}
}