using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using NetVips.Internal;
using GSignalMatchType = NetVips.Internal.Enums.GSignalMatchType;
namespace NetVips;
///
/// Manage lifetime.
///
public class GObject : SafeHandle
{
///
/// We have to record all of the delegates to
/// prevent them from being re-located or disposed of by the garbage collector.
///
///
/// All recorded delegates are freed in .
///
private readonly ICollection _handles = new List();
///
/// Hint of how much native memory is actually occupied by the object.
///
internal long MemoryPressure;
// Handy for debugging
// public static int NObjects;
///
/// Initializes a new instance of the class
/// with the specified pointer to wrap around.
///
///
/// Wraps a GObject instance around an underlying GValue. When the
/// instance is garbage-collected, the underlying object is unreferenced.
///
/// The pointer to wrap around.
internal GObject(nint pointer) : base(IntPtr.Zero, true)
{
// record the pointer we were given to manage
SetHandle(pointer);
// NObjects++;
}
///
/// Connects a callback function () to a signal on this object.
///
///
/// The callback will be triggered every time this signal is issued on this instance.
///
/// The type of the callback to connect.
/// A string of the form "signal-name::detail".
/// The callback to connect.
/// Data to pass to handler calls.
/// The handler id.
/// If it failed to connect the signal.
public ulong SignalConnect(string detailedSignal, T callback, nint data = default)
where T : notnull
{
// add a weak reference callback to ensure all handles are released on finalization
if (_handles.Count == 0)
{
GWeakNotify notify = ReleaseDelegates;
var notifyHandle = GCHandle.Alloc(notify);
Internal.GObject.WeakRef(this, notify, GCHandle.ToIntPtr(notifyHandle));
}
// prevent the delegate from being re-located or disposed of by the garbage collector
var delegateHandle = GCHandle.Alloc(callback);
_handles.Add(delegateHandle);
var cHandler = Marshal.GetFunctionPointerForDelegate(callback);
var ret = GSignal.ConnectData(this, detailedSignal, cHandler, data, null, default);
if (ret == 0)
{
throw new ArgumentException("Failed to connect signal " + detailedSignal);
}
return ret;
}
///
/// Disconnects a handler from this object.
///
///
/// If the is 0 then this function does nothing.
///
/// Handler id of the handler to be disconnected.
public void SignalHandlerDisconnect(ulong handlerId)
{
if (handlerId != 0)
{
GSignal.HandlerDisconnect(this, handlerId);
}
}
///
/// Disconnects all handlers from this object that match and
/// .
///
/// The type of the func.
/// The func of the handlers.
/// The data of the handlers.
/// The number of handlers that matched.
public uint SignalHandlersDisconnectByFunc(T func, nint data = default)
where T : notnull
{
var funcPtr = Marshal.GetFunctionPointerForDelegate(func);
return GSignal.HandlersDisconnectMatched(this,
GSignalMatchType.G_SIGNAL_MATCH_FUNC | GSignalMatchType.G_SIGNAL_MATCH_DATA,
0, 0, IntPtr.Zero, funcPtr, data);
}
///
/// Disconnects all handlers from this object that match .
///
/// The data of the handlers.
/// The number of handlers that matched.
public uint SignalHandlersDisconnectByData(nint data)
{
return GSignal.HandlersDisconnectMatched(this,
GSignalMatchType.G_SIGNAL_MATCH_DATA,
0, 0, IntPtr.Zero, IntPtr.Zero, data);
}
///
/// Decreases the reference count of object.
/// When its reference count drops to 0, the object is finalized (i.e. its memory is freed).
///
/// if the handle is released successfully; otherwise,
/// in the event of a catastrophic failure, .
protected override bool ReleaseHandle()
{
if (!IsInvalid)
{
Internal.GObject.Unref(handle);
}
// NObjects--;
return true;
}
///
/// Release all the delegates by this object on finalization.
///
///
/// This function is only called when was used on this object.
///
/// Data that was provided when the weak reference was established.
/// The object being disposed.
internal void ReleaseDelegates(nint data, nint objectPointer)
{
foreach (var gcHandle in _handles)
{
if (gcHandle.IsAllocated)
{
gcHandle.Free();
}
}
// All GCHandles are free'd. Clear the list to prevent inadvertent use.
_handles.Clear();
// Free the GCHandle used by this GWeakNotify
var notifyHandle = GCHandle.FromIntPtr(data);
if (notifyHandle.IsAllocated)
{
notifyHandle.Free();
}
}
///
/// Increases the reference count of object.
///
internal nint ObjectRef()
{
return Internal.GObject.Ref(handle);
}
///
/// Gets a value indicating whether the handle is invalid.
///
/// if the handle is not valid; otherwise, .
public override bool IsInvalid => handle == IntPtr.Zero;
///
/// Get the reference count of object. Handy for debugging.
///
internal uint RefCount => Marshal.PtrToStructure(handle).RefCount;
// Do not provide a finalizer - SafeHandle's critical finalizer will
// call ReleaseHandle for us.
}