// GLib.Signal.cs - signal marshaling class // // Authors: Mike Kestner // // Copyright (c) 2005 Novell, Inc. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the Lesser GNU General // Public License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this program; if not, write to the // Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307, USA. namespace GLib { using System; using System.Runtime.InteropServices; [Flags] internal enum SignalFlags { RunFirst = 1 << 0, RunLast = 1 << 1, RunCleanup = 1 << 2, NoRecurse = 1 << 3, Detailed = 1 << 4, Action = 1 << 5, NoHooks = 1 << 6 } [StructLayout (LayoutKind.Sequential)] internal struct InvocationHint { public uint signal_id; public uint detail; public SignalFlags run_type; } public class Signal { GCHandle gc_handle; IntPtr handle; string name; uint before_id = UInt32.MaxValue; uint after_id = UInt32.MaxValue; Delegate marshaler; static SignalDestroyNotify notify = new SignalDestroyNotify (OnNativeDestroy); [CDeclCallback] delegate void SignalDestroyNotify (IntPtr data, IntPtr obj); static void OnNativeDestroy (IntPtr data, IntPtr obj) { GCHandle gch = (GCHandle) data; Signal s = gch.Target as Signal; s.DisconnectHandler (s.before_id); s.DisconnectHandler (s.after_id); gch.Free (); } private Signal (GLib.Object obj, string signal_name, Delegate marshaler) { handle = obj.Handle; name = signal_name; this.marshaler = marshaler; gc_handle = GCHandle.Alloc (this); IntPtr native_key = GLib.Marshaller.StringToPtrGStrdup (name + "_signal_marshaler"); g_object_set_data_full (handle, native_key, (IntPtr) gc_handle, notify); GLib.Marshaller.Free (native_key); } public static Signal Lookup (GLib.Object obj, string name) { return Lookup (obj, name, EventHandlerDelegate); } public static Signal Lookup (GLib.Object obj, string name, Delegate marshaler) { IntPtr native_key = GLib.Marshaller.StringToPtrGStrdup (name + "_signal_marshaler"); IntPtr data = g_object_get_data (obj.Handle, native_key); GLib.Marshaller.Free (native_key); if (data == IntPtr.Zero) return new Signal (obj, name, marshaler); GCHandle gch = (GCHandle) data; return gch.Target as Signal; } Delegate before_handler; Delegate after_handler; public Delegate Handler { get { InvocationHint hint = (InvocationHint) Marshal.PtrToStructure (g_signal_get_invocation_hint (handle), typeof (InvocationHint)); if (hint.run_type == SignalFlags.RunFirst) return before_handler; else return after_handler; } } uint Connect (int flags) { IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name); uint id = g_signal_connect_data (handle, native_name, marshaler, (IntPtr) gc_handle, IntPtr.Zero, flags); GLib.Marshaller.Free (native_name); return id; } public void AddDelegate (Delegate d) { if (d.Method.IsDefined (typeof (ConnectBeforeAttribute), false)) { if (before_handler == null) { before_handler = d; before_id = Connect (0); } else before_handler = Delegate.Combine (before_handler, d); } else { if (after_handler == null) { after_handler = d; after_id = Connect (1); } else after_handler = Delegate.Combine (after_handler, d); } } public void RemoveDelegate (Delegate d) { if (d.Method.IsDefined (typeof (ConnectBeforeAttribute), false)) { before_handler = Delegate.Remove (before_handler, d); if (before_handler == null) { DisconnectHandler (before_id); before_id = UInt32.MaxValue; } } else { after_handler = Delegate.Remove (after_handler, d); if (after_handler == null) { DisconnectHandler (after_id); after_id = UInt32.MaxValue; } } if (after_id == UInt32.MaxValue && before_id == UInt32.MaxValue) DisconnectObject (); } void DisconnectObject () { IntPtr native_key = GLib.Marshaller.StringToPtrGStrdup (name + "_signal_marshaler"); g_object_set_data (handle, native_key, IntPtr.Zero); GLib.Marshaller.Free (native_key); } void DisconnectHandler (uint handler_id) { if (handler_id != UInt32.MaxValue && g_signal_handler_is_connected (handle, handler_id)) g_signal_handler_disconnect (handle, handler_id); } [CDeclCallback] delegate void voidObjectDelegate (IntPtr handle, IntPtr gch); static void voidObjectCallback (IntPtr handle, IntPtr gch) { Signal sig = ((GCHandle) gch).Target as Signal; if (sig == null) throw new Exception ("Unknown signal class GC handle received."); EventHandler handler = (EventHandler) sig.Handler; handler (Object.GetObject (handle), EventArgs.Empty); } static voidObjectDelegate event_handler_delegate; static voidObjectDelegate EventHandlerDelegate { get { if (event_handler_delegate == null) event_handler_delegate = new voidObjectDelegate (voidObjectCallback); return event_handler_delegate; } } [DllImport("libgobject-2.0-0.dll")] static extern IntPtr g_object_get_data (IntPtr instance, IntPtr key); [DllImport("libgobject-2.0-0.dll")] static extern void g_object_set_data (IntPtr instance, IntPtr key, IntPtr data); [DllImport("libgobject-2.0-0.dll")] static extern void g_object_set_data_full (IntPtr instance, IntPtr key, IntPtr data, SignalDestroyNotify notify); [DllImport("libgobject-2.0-0.dll")] static extern uint g_signal_connect_data(IntPtr obj, IntPtr name, Delegate cb, IntPtr gc_handle, IntPtr dummy, int flags); [DllImport("libgobject-2.0-0.dll")] static extern IntPtr g_signal_get_invocation_hint (IntPtr instance); [DllImport("libgobject-2.0-0.dll")] static extern void g_signal_handler_disconnect (IntPtr instance, uint handler); [DllImport("libgobject-2.0-0.dll")] static extern bool g_signal_handler_is_connected (IntPtr instance, uint handler); } }