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. }