diff --git a/src/Libs/GObject-2.0/Public/Object2.cs b/src/Libs/GObject-2.0/Public/Object2.cs new file mode 100644 index 000000000..76a6c3507 --- /dev/null +++ b/src/Libs/GObject-2.0/Public/Object2.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using GLib; +using GObject.Internal; + +namespace GObject +{ + public class Object2 : IDisposable + { + private readonly Object2Handle _handle; + + public Object2(Object2Handle handle) + { + _handle = handle; + _handle.AddMemoryPressure(); + } + + public IntPtr GetHandle() => _handle.DangerousGetHandle(); + + public void Dispose() + { + _handle.Dispose(); + } + } +} + + +namespace GObject.Internal +{ + public class ToggleRef2 : IDisposable + { + private readonly IntPtr _handle; + private readonly ToggleNotify _callback; + + private object _reference; + + public object? Object + { + get + { + if(_reference is WeakReference weakRef) + return weakRef.Target; + + return _reference; + } + } + + /// + /// Initializes a toggle ref. The given object must be already owned by C# as the owned + /// reference is exchanged with a toggling reference meaning the toggle reference is taking control + /// over the reference. + /// This object saves a strong reference to the given object which prevents it from beeing garbage + /// collected. This strong reference is hold as long as there are other than our own toggling ref + /// on the given object. + /// If our toggeling ref is the last ref on the given object the strong reference is changed into a + /// weak reference. This allows the garbage collector to free the C# object which must result in the + /// call of the Dispose method of the ToggleRef. The Dispose method removes the added toggle reference + /// and thus frees the last reference to the C object. + /// + public ToggleRef2(Object2 obj) + { + _reference = obj; + _handle = obj.GetHandle(); + + _callback = ToggleReference; + + RegisterToggleRef(); + } + + private void RegisterToggleRef() + { + Internal.Object.AddToggleRef(_handle, _callback, IntPtr.Zero); + Internal.Object.Unref(_handle); + } + + private void ToggleReference(IntPtr data, IntPtr @object, bool isLastRef) + { + if (!isLastRef && _reference is WeakReference weakRef) + { + if (weakRef.Target is { } weakObj) + _reference = weakObj; + else + throw new Exception($"Handle {_handle}: Could not toggle reference to strong. It got garbage collected."); + } + else if (isLastRef && _reference is not WeakReference) + { + _reference = new WeakReference(_reference); + } + } + + public void Dispose() + { + var sourceFunc = new GLib.Internal.SourceFuncAsyncHandler(() => + { + Internal.Object.RemoveToggleRef(_handle, _callback, IntPtr.Zero); + return false; + }); + GLib.Internal.MainContext.Invoke(GLib.Internal.MainContextUnownedHandle.NullHandle, sourceFunc.NativeCallback, IntPtr.Zero); + } + } + + public class ObjectMapper2 + { + private static readonly Dictionary WrapperObjects = new(); + + public static bool TryGetObject(IntPtr handle, [NotNullWhen(true)] out T? obj) where T : Object2 + { + if (WrapperObjects.TryGetValue(handle, out ToggleRef2? toggleRef)) + { + if (toggleRef.Object is not null) + { + obj = (T) toggleRef.Object; + return true; + } + } + + obj = null; + return false; + } + } + + public class ObjectWrapper2 + { + public static T? WrapNullableHandle(IntPtr handle, bool ownedRef) where T : Object2 + { + return handle == IntPtr.Zero + ? null + : WrapHandle(handle, ownedRef); + } + + public static T WrapHandle(IntPtr handle, bool ownedRef) where T : Object2 + { + if (handle == IntPtr.Zero) + throw new NullReferenceException($"Failed to wrap handle as type <{typeof(T).FullName}>. Null handle passed to WrapHandle."); + + if (ObjectMapper2.TryGetObject(handle, out T? obj)) + return obj; + } + } + + public class Object2Handle : SafeHandle + { + public override bool IsInvalid => handle == IntPtr.Zero; + + public Object2Handle(IntPtr handle, bool ownsHandle) : base(IntPtr.Zero, true) + { + SetHandle(handle); + OwnReference(ownsHandle); + } + + private void OwnReference(bool ownedRef) + { + if (!ownedRef) + { + // - Unowned GObjects need to be refed to bind them to this instance + // - Unowned InitiallyUnowned floating objects need to be ref_sinked + // - Unowned InitiallyUnowned non-floating objects need to be refed + // As ref_sink behaves like ref in case of non floating instances we use it for all 3 cases + Object.RefSink(handle); + } + else + { + //In case we own the ref because the ownership was fully transfered to us we + //do not need to ref the object at all. + + Debug.Assert(!Internal.Object.IsFloating(handle), $"Handle {handle}: Owned floating references are not possible."); + } + } + + protected override bool ReleaseHandle() + { + RemoveMemoryPressure(); + Object.Unref(handle); + return true; + } + + protected internal virtual void AddMemoryPressure() { } + protected virtual void RemoveMemoryPressure() { } + } +} + + + diff --git a/src/Libs/GdkPixbuf-2.0/Public/Pixbuf2.cs b/src/Libs/GdkPixbuf-2.0/Public/Pixbuf2.cs new file mode 100644 index 000000000..934d69c7e --- /dev/null +++ b/src/Libs/GdkPixbuf-2.0/Public/Pixbuf2.cs @@ -0,0 +1,55 @@ +using System; +using GdkPixbuf.Internal; +using GObject.Internal; + +namespace GdkPixbuf +{ + public class Pixbuf2 : GObject.Object2 + { + public Pixbuf2(Pixbuf2Handle handle) : base(handle) { } + + public static Pixbuf2 New(Colorspace colorspace, bool hasAlpha, int bitsPerSample, int width, int height) + { + //TODO: How is the instance kept alive in case C# does not need it anymore, but C does? + var handle = Internal.Pixbuf.New(colorspace, hasAlpha, bitsPerSample, width, height); + return new Pixbuf2(new Pixbuf2Handle(handle, true)); + } + + + [Version("2.12")] + public Pixbuf2? ApplyEmbeddedOrientation() + { + var resultApplyEmbeddedOrientation = GdkPixbuf.Internal.Pixbuf.ApplyEmbeddedOrientation(GetHandle()); + + + + return ObjectWrapper2.WrapNullableHandle(resultApplyEmbeddedOrientation, true); + } + } +} + +namespace GdkPixbuf.Internal +{ + public class Pixbuf2Handle : GObject.Internal.Object2Handle + { + private long _size; + + public Pixbuf2Handle(IntPtr handle, bool ownsHandle) : base(handle, ownsHandle) + { + } + + protected override void AddMemoryPressure() + { + _size = (long) Internal.Pixbuf.GetByteLength(handle); + GC.AddMemoryPressure(_size); + } + + protected override void RemoveMemoryPressure() + { + GC.RemoveMemoryPressure(_size); + } + } +} + + +