CPF/CPF.Mac/Mac/CoreFoundation/CFRunLoopSourceCustom.cs

102 lines
2.8 KiB
C#
Raw Normal View History

2023-11-21 23:05:03 +08:00
using System;
using System.Runtime.InteropServices;
namespace CPF.Mac.CoreFoundation
{
public abstract class CFRunLoopSourceCustom : CFRunLoopSource
{
private delegate void ScheduleCallback(IntPtr info, IntPtr runLoop, IntPtr mode);
private delegate void CancelCallback(IntPtr info, IntPtr runLoop, IntPtr mode);
private delegate void PerformCallback(IntPtr info);
private GCHandle gch;
[DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
private static extern IntPtr CFRunLoopSourceCreate(IntPtr allocator, int order, IntPtr context);
protected CFRunLoopSourceCustom()
: base(IntPtr.Zero, ownsHandle: true)
{
gch = GCHandle.Alloc(this);
CFRunLoopSourceContext structure = new CFRunLoopSourceContext
{
Info = GCHandle.ToIntPtr(gch),
Schedule = Marshal.GetFunctionPointerForDelegate<ScheduleCallback>(Schedule),
Cancel = Marshal.GetFunctionPointerForDelegate<CancelCallback>(Cancel),
Perform = Marshal.GetFunctionPointerForDelegate<PerformCallback>(Perform)
};
IntPtr intPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CFRunLoopSourceContext)));
try
{
Marshal.StructureToPtr(structure, intPtr, fDeleteOld: false);
handle = CFRunLoopSourceCreate(IntPtr.Zero, 0, intPtr);
}
finally
{
Marshal.FreeHGlobal(intPtr);
}
if (handle == IntPtr.Zero)
{
throw new NotSupportedException();
}
}
[MonoPInvokeCallback(typeof(ScheduleCallback))]
private static void Schedule(IntPtr info, IntPtr runLoop, IntPtr mode)
{
CFRunLoopSourceCustom cFRunLoopSourceCustom = GCHandle.FromIntPtr(info).Target as CFRunLoopSourceCustom;
CFRunLoop cFRunLoop = new CFRunLoop(runLoop);
CFString cFString = new CFString(mode);
try
{
cFRunLoopSourceCustom.OnSchedule(cFRunLoop, cFString);
}
finally
{
cFRunLoop.Dispose();
cFString.Dispose();
}
}
protected abstract void OnSchedule(CFRunLoop loop, string mode);
[MonoPInvokeCallback(typeof(CancelCallback))]
private static void Cancel(IntPtr info, IntPtr runLoop, IntPtr mode)
{
CFRunLoopSourceCustom cFRunLoopSourceCustom = GCHandle.FromIntPtr(info).Target as CFRunLoopSourceCustom;
CFRunLoop cFRunLoop = new CFRunLoop(runLoop);
CFString cFString = new CFString(mode);
try
{
cFRunLoopSourceCustom.OnCancel(cFRunLoop, cFString);
}
finally
{
cFRunLoop.Dispose();
cFString.Dispose();
}
}
protected abstract void OnCancel(CFRunLoop loop, string mode);
[MonoPInvokeCallback(typeof(PerformCallback))]
private static void Perform(IntPtr info)
{
(GCHandle.FromIntPtr(info).Target as CFRunLoopSourceCustom).OnPerform();
}
protected abstract void OnPerform();
public override void Dispose(bool disposing)
{
if (disposing && gch.IsAllocated)
{
gch.Free();
}
base.Dispose(disposing);
}
}
}