1 // SignalClosure.cs - signal marshaling class
2 //
3 // Authors: Mike Kestner <mkestner@novell.com>
4 //
5 // Copyright (c) 2008 Novell, Inc.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of version 2 of the Lesser GNU General
9 // Public License as published by the Free Software Foundation.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this program; if not, write to the
18 // Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 // Boston, MA 02111-1307, USA.
20 
21 
22 namespace GLib {
23 
24 	using System;
25 	using System.Collections;
26 	using System.Collections.Generic;
27 	using System.Runtime.InteropServices;
28 
29 	internal class ClosureInvokedArgs : EventArgs {
30 
31 		EventArgs args;
32 		GLib.Object obj;
33 		object result;
34 
ClosureInvokedArgs(GLib.Object obj, EventArgs args)35 		public ClosureInvokedArgs (GLib.Object obj, EventArgs args)
36 		{
37 			this.obj = obj;
38 			this.args = args;
39 		}
40 
41 		public EventArgs Args {
42 			get {
43 				return args;
44 			}
45 		}
46 
47 		public GLib.Object Target {
48 			get {
49 				return obj;
50 			}
51 		}
52 	}
53 
ClosureInvokedHandler(object o, ClosureInvokedArgs args)54 	internal delegate void ClosureInvokedHandler (object o, ClosureInvokedArgs args);
55 
56 	public partial class Signal
57 	{
58 		class SignalClosure : IDisposable
59 		{
60 			internal Signal signal;
61 			IntPtr raw_closure;
62 			uint id = UInt32.MaxValue;
63 			GCHandle? gch;
64 
65 			static Dictionary<IntPtr, SignalClosure> closures = new Dictionary<IntPtr, SignalClosure> (IntPtrEqualityComparer.Instance);
66 
SignalClosure(Signal sig, System.Type args_type)67 			public SignalClosure (Signal sig, System.Type args_type)
68 			{
69 				raw_closure = glibsharp_closure_new (Marshaler, Notify, IntPtr.Zero);
70 				closures [raw_closure] = this;
71 				signal = sig;
72 			}
73 
SignalClosure(Signal sig, Delegate custom_marshaler)74 			public SignalClosure (Signal sig, Delegate custom_marshaler)
75 			{
76 				gch = GCHandle.Alloc (sig);
77 				raw_closure = g_cclosure_new (custom_marshaler, (IntPtr)gch, Notify);
78 				closures [raw_closure] = this;
79 				signal = sig;
80 			}
81 
82 			public event EventHandler Disposed;
83 			public event ClosureInvokedHandler Invoked;
84 
Connect(bool is_after)85 			public void Connect (bool is_after)
86 			{
87 				IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (signal.name);
88 				id = g_signal_connect_closure (signal.tref.Handle, native_name, raw_closure, is_after);
89 				GLib.Marshaller.Free (native_name);
90 			}
91 
Disconnect()92 			public void Disconnect ()
93 			{
94 				if (id != UInt32.MaxValue && g_signal_handler_is_connected (signal.tref.Handle, id))
95 					g_signal_handler_disconnect (signal.tref.Handle, id);
96 			}
97 
Dispose()98 			public void Dispose ()
99 			{
100 				Disconnect ();
101 				closures.Remove (raw_closure);
102 				if (gch != null) {
103 					gch.Value.Free ();
104 					gch = null;
105 				}
106 				if (Disposed != null)
107 					Disposed (this, EventArgs.Empty);
108 				GC.SuppressFinalize (this);
109 			}
110 
Invoke(ClosureInvokedArgs args)111 			public void Invoke (ClosureInvokedArgs args)
112 			{
113 				if (Invoked == null)
114 					return;
115 				Invoked (this, args);
116 			}
117 
118 			static ClosureMarshal marshaler;
119 			static ClosureMarshal Marshaler {
120 				get {
121 					if (marshaler == null) {
122 						unsafe
123 						{
124 							marshaler = new ClosureMarshal (MarshalCallback);
125 						}
126 					}
127 					return marshaler;
128 				}
129 			}
130 
131 			[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
ClosureMarshal(IntPtr closure, Value* return_val, uint n_param_vals, Value* param_values, IntPtr invocation_hint, IntPtr marshal_data)132 			unsafe delegate void ClosureMarshal (IntPtr closure, Value* return_val, uint n_param_vals, Value* param_values, IntPtr invocation_hint, IntPtr marshal_data);
133 
MarshalCallback(IntPtr raw_closure, Value* return_val, uint n_param_vals, Value* param_values, IntPtr invocation_hint, IntPtr marshal_data)134 			unsafe static void MarshalCallback (IntPtr raw_closure, Value* return_val, uint n_param_vals, Value* param_values, IntPtr invocation_hint, IntPtr marshal_data)
135 			{
136 				SignalClosure closure = null;
137 				try {
138 					closure = closures [raw_closure] as SignalClosure;
139 					GLib.Object __obj = param_values [0].Val as GLib.Object;
140 					if (__obj == null)
141 						return;
142 
143 					if (closure.signal.args_type == typeof (EventArgs)) {
144 						closure.Invoke (new ClosureInvokedArgs (__obj, EventArgs.Empty));
145 						return;
146 					}
147 
148 					SignalArgs args = FastActivator.CreateSignalArgs (closure.signal.args_type);
149 					args.Args = new object [n_param_vals - 1];
150 					for (int i = 1; i < n_param_vals; i++) {
151 						args.Args [i - 1] = param_values [i].Val;
152 					}
153 					ClosureInvokedArgs ci_args = new ClosureInvokedArgs (__obj, args);
154 					closure.Invoke (ci_args);
155 					for (int i = 1; i < n_param_vals; i++) {
156 						param_values [i].Update (args.Args [i - 1]);
157 					}
158 					if (return_val == null || args.RetVal == null)
159 						return;
160 
161 					return_val->Val = args.RetVal;
162 				} catch (Exception e) {
163 					if (closure != null)
164 						Console.WriteLine ("Marshaling {0} signal", closure.signal.name);
165 					ExceptionManager.RaiseUnhandledException (e, false);
166 				}
167 			}
168 
169 			[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
ClosureNotify(IntPtr data, IntPtr closure)170 			delegate void ClosureNotify (IntPtr data, IntPtr closure);
171 
NotifyCallback(IntPtr data, IntPtr raw_closure)172 			static void NotifyCallback (IntPtr data, IntPtr raw_closure)
173 			{
174 				SignalClosure closure;
175 				if (closures.TryGetValue (raw_closure, out closure))
176 					closure.Dispose ();
177 			}
178 
179 			static ClosureNotify notify_handler;
180 			static ClosureNotify Notify {
181 				get {
182 					if (notify_handler == null)
183 						notify_handler = new ClosureNotify (NotifyCallback);
184 					return notify_handler;
185 				}
186 			}
187 
188 			[DllImport ("glibsharpglue-2", CallingConvention = CallingConvention.Cdecl)]
glibsharp_closure_new(ClosureMarshal marshaler, ClosureNotify notify, IntPtr gch)189 			static extern IntPtr glibsharp_closure_new (ClosureMarshal marshaler, ClosureNotify notify, IntPtr gch);
190 
191 			[DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
g_cclosure_new(Delegate cb, IntPtr user_data, ClosureNotify notify)192 			static extern IntPtr g_cclosure_new (Delegate cb, IntPtr user_data, ClosureNotify notify);
193 
194 			[DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
g_signal_connect_closure(IntPtr obj, IntPtr name, IntPtr closure, bool is_after)195 			static extern uint g_signal_connect_closure (IntPtr obj, IntPtr name, IntPtr closure, bool is_after);
196 
197 			[DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
g_signal_handler_disconnect(IntPtr instance, uint handler)198 			static extern void g_signal_handler_disconnect (IntPtr instance, uint handler);
199 
200 			[DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
g_signal_handler_is_connected(IntPtr instance, uint handler)201 			static extern bool g_signal_handler_is_connected (IntPtr instance, uint handler);
202 		}
203 	}
204 }
205