GtkSharp/glib/SignalClosure.cs

227 lines
7.1 KiB
C#
Raw Normal View History

// SignalClosure.cs - signal marshaling class
//
// Authors: Mike Kestner <mkestner@novell.com>
//
// Copyright (c) 2008 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.Collections;
using System.Runtime.InteropServices;
internal class ClosureInvokedArgs : EventArgs {
EventArgs args;
GLib.Object obj;
public ClosureInvokedArgs (GLib.Object obj, EventArgs args)
{
this.obj = obj;
this.args = args;
}
public EventArgs Args {
get {
return args;
}
}
public GLib.Object Target {
get {
return obj;
}
}
}
struct GClosure {
public long fields;
public IntPtr marshaler;
public IntPtr data;
public IntPtr notifiers;
}
internal delegate void ClosureInvokedHandler (object o, ClosureInvokedArgs args);
internal class SignalClosure : IDisposable {
IntPtr handle;
IntPtr raw_closure;
string name;
uint id = UInt32.MaxValue;
System.Type args_type;
Delegate custom_marshaler;
GCHandle gch;
static Hashtable closures = new Hashtable ();
public SignalClosure (IntPtr obj, string signal_name, System.Type args_type)
{
raw_closure = g_closure_new_simple (Marshal.SizeOf (typeof (GClosure)), IntPtr.Zero);
g_closure_set_marshal (raw_closure, Marshaler);
g_closure_add_finalize_notifier (raw_closure, IntPtr.Zero, Notify);
closures [raw_closure] = this;
handle = obj;
name = signal_name;
this.args_type = args_type;
}
public SignalClosure (IntPtr obj, string signal_name, Delegate custom_marshaler, Signal signal)
{
gch = GCHandle.Alloc (signal);
raw_closure = g_cclosure_new (custom_marshaler, (IntPtr) gch, Notify);
closures [raw_closure] = this;
handle = obj;
name = signal_name;
this.custom_marshaler = custom_marshaler;
}
public event EventHandler Disposed;
public event ClosureInvokedHandler Invoked;
public void Connect (bool is_after)
{
IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name);
id = g_signal_connect_closure (handle, native_name, raw_closure, is_after);
GLib.Marshaller.Free (native_name);
}
public void Disconnect ()
{
if (id != UInt32.MaxValue && g_signal_handler_is_connected (handle, id))
g_signal_handler_disconnect (handle, id);
}
public void Dispose ()
{
Disconnect ();
closures.Remove (raw_closure);
if (custom_marshaler != null)
gch.Free ();
custom_marshaler = null;
if (Disposed != null)
Disposed (this, EventArgs.Empty);
GC.SuppressFinalize (this);
}
public void Invoke (ClosureInvokedArgs args)
{
if (Invoked == null)
return;
Invoked (this, args);
}
static ClosureMarshal marshaler;
static ClosureMarshal Marshaler {
get {
if (marshaler == null)
marshaler = new ClosureMarshal (MarshalCallback);
return marshaler;
}
}
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
delegate void ClosureMarshal (IntPtr closure, IntPtr return_val, uint n_param_vals, IntPtr param_values, IntPtr invocation_hint, IntPtr marshal_data);
static void MarshalCallback (IntPtr raw_closure, IntPtr return_val, uint n_param_vals, IntPtr param_values, IntPtr invocation_hint, IntPtr marshal_data)
{
string message = String.Empty;
try {
SignalClosure closure = closures [raw_closure] as SignalClosure;
message = "Marshaling " + closure.name + " signal";
Value objval = (Value) Marshal.PtrToStructure (param_values, typeof (Value));
GLib.Object __obj = objval.Val as GLib.Object;
if (__obj == null)
return;
if (closure.args_type == typeof (EventArgs)) {
closure.Invoke (new ClosureInvokedArgs (__obj, EventArgs.Empty));
return;
}
SignalArgs args = Activator.CreateInstance (closure.args_type, new object [0]) as SignalArgs;
args.Args = new object [n_param_vals - 1];
GLib.Value[] vals = new GLib.Value [n_param_vals - 1];
for (int i = 1; i < n_param_vals; i++) {
IntPtr ptr = new IntPtr (param_values.ToInt64 () + i * Marshal.SizeOf (typeof (Value)));
vals [i - 1] = (Value) Marshal.PtrToStructure (ptr, typeof (Value));
args.Args [i - 1] = vals [i - 1].Val;
}
ClosureInvokedArgs ci_args = new ClosureInvokedArgs (__obj, args);
closure.Invoke (ci_args);
for (int i = 1; i < n_param_vals; i++) {
vals [i - 1].Update (args.Args [i - 1]);
IntPtr ptr = new IntPtr (param_values.ToInt64 () + i * Marshal.SizeOf (typeof (Value)));
Marshal.StructureToPtr (vals [i - 1], ptr, false);
}
if (return_val == IntPtr.Zero || args.RetVal == null)
return;
Value ret = (Value) Marshal.PtrToStructure (return_val, typeof (Value));
ret.Val = args.RetVal;
Marshal.StructureToPtr (ret, return_val, false);
} catch (Exception e) {
Console.WriteLine (message);
ExceptionManager.RaiseUnhandledException (e, false);
}
}
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
delegate void ClosureNotify (IntPtr data, IntPtr closure);
static void NotifyCallback (IntPtr data, IntPtr raw_closure)
{
SignalClosure closure = closures [raw_closure] as SignalClosure;
if (closure != null)
closure.Dispose ();
}
static ClosureNotify notify_handler;
static ClosureNotify Notify {
get {
if (notify_handler == null)
notify_handler = new ClosureNotify (NotifyCallback);
return notify_handler;
}
}
[DllImport (Global.GObjectNativeDll, CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr g_cclosure_new (Delegate cb, IntPtr user_data, ClosureNotify notify);
[DllImport (Global.GObjectNativeDll, CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr g_closure_new_simple (int closure_size, IntPtr dummy);
[DllImport (Global.GObjectNativeDll, CallingConvention = CallingConvention.Cdecl)]
static extern void g_closure_set_marshal (IntPtr closure, ClosureMarshal marshaler);
[DllImport (Global.GObjectNativeDll, CallingConvention = CallingConvention.Cdecl)]
static extern void g_closure_add_finalize_notifier (IntPtr closure, IntPtr dummy, ClosureNotify notify);
[DllImport (Global.GObjectNativeDll, CallingConvention = CallingConvention.Cdecl)]
static extern uint g_signal_connect_closure (IntPtr obj, IntPtr name, IntPtr closure, bool is_after);
[DllImport (Global.GObjectNativeDll, CallingConvention = CallingConvention.Cdecl)]
static extern void g_signal_handler_disconnect (IntPtr instance, uint handler);
[DllImport (Global.GObjectNativeDll, CallingConvention = CallingConvention.Cdecl)]
static extern bool g_signal_handler_is_connected (IntPtr instance, uint handler);
}
}