1 // GtkSharp.Generation.Signal.cs - The Signal Generatable. 2 // 3 // Author: Mike Kestner <mkestner@speakeasy.net> 4 // 5 // Copyright (c) 2001-2003 Mike Kestner 6 // Copyright (c) 2003-2005 Novell, Inc. 7 // Copyright (c) 2007 Novell, Inc. 8 // 9 // This program is free software; you can redistribute it and/or 10 // modify it under the terms of version 2 of the GNU General Public 11 // License as published by the Free Software Foundation. 12 // 13 // This program is distributed in the hope that it will be useful, 14 // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 // General Public License for more details. 17 // 18 // You should have received a copy of the GNU General Public 19 // License along with this program; if not, write to the 20 // Free Software Foundation, Inc., 59 Temple Place - Suite 330, 21 // Boston, MA 02111-1307, USA. 22 23 24 namespace GtkSharp.Generation { 25 26 using System; 27 using System.Collections; 28 using System.Collections.Generic; 29 using System.IO; 30 using System.Xml; 31 32 public class Signal { 33 34 bool marshaled; 35 string name; 36 XmlElement elem; 37 ReturnValue retval; 38 Parameters parms; 39 ObjectBase container_type; 40 Signal(XmlElement elem, ObjectBase container_type)41 public Signal (XmlElement elem, ObjectBase container_type) 42 { 43 this.elem = elem; 44 name = elem.GetAttribute ("name"); 45 marshaled = elem.GetAttribute ("manual") == "true"; 46 retval = new ReturnValue (elem ["return-type"]); 47 parms = new Parameters (elem["parameters"], container_type.ParserVersion == 1 ? true : false); 48 this.container_type = container_type; 49 } 50 51 bool Marshaled { 52 get { return marshaled; } 53 } 54 55 public string Name { 56 get { 57 return name; 58 } 59 set { 60 name = value; 61 } 62 } 63 Validate(LogWriter log)64 public bool Validate (LogWriter log) 65 { 66 log.Member = Name; 67 if (Name == "") { 68 log.Warn ("Nameless signal found. Add name attribute with fixup."); 69 Statistics.ThrottledCount++; 70 return false; 71 } else if (!parms.Validate (log) || !retval.Validate (log)) { 72 Statistics.ThrottledCount++; 73 return false; 74 } 75 return true; 76 } 77 GenerateDecl(StreamWriter sw)78 public void GenerateDecl (StreamWriter sw) 79 { 80 if (elem.GetAttributeAsBoolean ("new_flag") || (container_type != null && container_type.GetSignalRecursively (Name) != null)) 81 sw.Write("new "); 82 83 sw.WriteLine ("\t\tevent " + EventHandlerQualifiedName + " " + Name + ";"); 84 } 85 86 public string CName { 87 get { 88 return "\"" + elem.GetAttribute("cname") + "\""; 89 } 90 } 91 92 string CallbackSig { 93 get { 94 string result = ""; 95 for (int i = 0; i < parms.Count; i++) { 96 if (i > 0) 97 result += ", "; 98 99 Parameter p = parms [i]; 100 if (p.PassAs != "" && !(p.Generatable is StructBase)) 101 result += p.PassAs + " "; 102 result += (p.MarshalType + " arg" + i); 103 } 104 105 return result; 106 } 107 } 108 109 string CallbackName { 110 get { return Name + "SignalCallback"; } 111 } 112 113 string DelegateName { 114 get { return Name + "SignalDelegate"; } 115 } 116 117 private string EventArgsName { 118 get { 119 if (IsEventHandler) 120 return "EventArgs"; 121 else 122 return Name + "Args"; 123 } 124 } 125 126 private string EventArgsQualifiedName { 127 get { 128 if (IsEventHandler) 129 return "System.EventArgs"; 130 else 131 return container_type.NS + "." + Name + "Args"; 132 } 133 } 134 135 private string EventHandlerName { 136 get { 137 if (IsEventHandler) 138 return "EventHandler"; 139 else if (SymbolTable.Table [container_type.NS + Name + "Handler"] != null) 140 return Name + "EventHandler"; 141 else 142 return Name + "Handler"; 143 } 144 } 145 146 private string EventHandlerQualifiedName { 147 get { 148 if (IsEventHandler) 149 return "System.EventHandler"; 150 else 151 return container_type.NS + "." + EventHandlerName; 152 } 153 } 154 155 private bool IsEventHandler { 156 get { 157 return retval.CSType == "void" && parms.Count == 0; 158 } 159 } 160 GenArgsInitialization(StreamWriter sw, IList<Parameter> dispose_params)161 private string GenArgsInitialization (StreamWriter sw, IList<Parameter> dispose_params) 162 { 163 if (parms.Count > 0) 164 sw.WriteLine("\t\t\t\targs.Args = new object[" + parms.Count + "];"); 165 string finish = ""; 166 for (int idx = 0; idx < parms.Count; idx++) { 167 Parameter p = parms [idx]; 168 IGeneratable igen = p.Generatable; 169 if (p.PassAs != "out") { 170 if (igen is ManualGen) { 171 sw.WriteLine("\t\t\t\tif (arg{0} == IntPtr.Zero)", idx); 172 sw.WriteLine("\t\t\t\t\targs.Args[{0}] = null;", idx); 173 sw.WriteLine("\t\t\t\telse {"); 174 sw.WriteLine("\t\t\t\t\targs.Args[" + idx + "] = " + p.FromNative ("arg" + idx) + ";"); 175 sw.WriteLine("\t\t\t\t}"); 176 } else if (dispose_params.Contains (p)) { 177 sw.WriteLine("\t\t\t\t" + p.Name + " = " + p.FromNative ("arg" + idx) + ";"); 178 sw.WriteLine("\t\t\t\targs.Args[" + idx + "] = " + p.Name + ";"); 179 } else { 180 sw.WriteLine("\t\t\t\targs.Args[" + idx + "] = " + p.FromNative ("arg" + idx) + ";"); 181 } 182 } 183 if ((igen is StructBase || igen is ByRefGen) && p.PassAs != "") 184 finish += "\t\t\t\tif (arg" + idx + " != IntPtr.Zero) System.Runtime.InteropServices.Marshal.StructureToPtr (args.Args[" + idx + "], arg" + idx + ", false);\n"; 185 else if (igen is IManualMarshaler && p.PassAs != "") 186 finish += String.Format ("\t\t\t\targ{0} = {1};\n", idx, (igen as IManualMarshaler).AllocNative ("args.Args[" + idx + "]")); 187 else if (p.PassAs != "") 188 finish += "\t\t\t\targ" + idx + " = " + igen.CallByName ("((" + p.CSType + ")args.Args[" + idx + "])") + ";\n"; 189 } 190 return finish; 191 } 192 GenArgsCleanup(StreamWriter sw, string finish)193 private void GenArgsCleanup (StreamWriter sw, string finish) 194 { 195 if (retval.IsVoid && finish.Length == 0) 196 return; 197 198 sw.WriteLine("\n\t\t\ttry {"); 199 sw.Write (finish); 200 if (!retval.IsVoid) { 201 if (retval.CSType == "bool") { 202 sw.WriteLine ("\t\t\t\tif (args.RetVal == null)"); 203 sw.WriteLine ("\t\t\t\t\treturn false;"); 204 } 205 sw.WriteLine ("\t\t\t\treturn {0};", retval.ToNative (String.Format ("(({0}) args.RetVal)", retval.CSType))); 206 } 207 sw.WriteLine("\t\t\t} catch (Exception) {"); 208 sw.WriteLine ("\t\t\t\tException ex = new Exception (\"args.RetVal or 'out' property unset or set to incorrect type in " + EventHandlerQualifiedName + " callback\");"); 209 sw.WriteLine("\t\t\t\tGLib.ExceptionManager.RaiseUnhandledException (ex, true);"); 210 211 sw.WriteLine ("\t\t\t\t// NOTREACHED: above call doesn't return."); 212 sw.WriteLine ("\t\t\t\tthrow ex;"); 213 sw.WriteLine("\t\t\t}"); 214 } 215 GenCallback(StreamWriter sw)216 public void GenCallback (StreamWriter sw) 217 { 218 if (IsEventHandler) 219 return; 220 221 IList<Parameter> dispose_params = new List<Parameter> (); 222 foreach (Parameter p in parms) { 223 if (p.IsOwnable) { 224 dispose_params.Add (p); 225 } 226 } 227 228 string native_signature = "IntPtr inst"; 229 if (parms.Count > 0) 230 native_signature += ", " + CallbackSig; 231 native_signature += ", IntPtr gch"; 232 233 sw.WriteLine ("\t\t[UnmanagedFunctionPointer (CallingConvention.Cdecl)]"); 234 sw.WriteLine ("\t\tdelegate {0} {1} ({2});", retval.ToNativeType, DelegateName, native_signature); 235 sw.WriteLine (); 236 sw.WriteLine ("\t\tstatic {0} {1} ({2})", retval.ToNativeType, CallbackName, native_signature); 237 sw.WriteLine("\t\t{"); 238 sw.WriteLine("\t\t\t{0} args = new {0} ();", EventArgsQualifiedName); 239 foreach (Parameter p in dispose_params) { 240 sw.WriteLine("\t\t\t{0} {1} = null;", p.CSType, p.Name); 241 } 242 sw.WriteLine("\t\t\ttry {"); 243 sw.WriteLine("\t\t\t\tGLib.Signal sig = ((GCHandle) gch).Target as GLib.Signal;"); 244 sw.WriteLine("\t\t\t\tif (sig == null)"); 245 sw.WriteLine("\t\t\t\t\tthrow new Exception(\"Unknown signal GC handle received \" + gch);"); 246 sw.WriteLine(); 247 string finish = GenArgsInitialization (sw, dispose_params); 248 sw.WriteLine("\t\t\t\t{0} handler = ({0}) sig.Handler;", EventHandlerQualifiedName); 249 sw.WriteLine("\t\t\t\thandler (GLib.Object.GetObject (inst), args);"); 250 sw.WriteLine("\t\t\t} catch (Exception e) {"); 251 sw.WriteLine("\t\t\t\tGLib.ExceptionManager.RaiseUnhandledException (e, false);"); 252 if (dispose_params.Count > 0) { 253 sw.WriteLine ("\t\t\t} finally {"); 254 foreach (Parameter p in dispose_params) { 255 string disp_name = "disposable_" + p.Name; 256 257 sw.WriteLine ("\t\t\t\tvar " + disp_name + " = " + p.Name + " as IDisposable;"); 258 sw.WriteLine ("\t\t\t\tif (" + disp_name + " != null)"); 259 sw.WriteLine ("\t\t\t\t\t" + disp_name + ".Dispose ();"); 260 } 261 } 262 sw.WriteLine ("\t\t\t}"); 263 GenArgsCleanup (sw, finish); 264 sw.WriteLine("\t\t}"); 265 sw.WriteLine(); 266 } 267 NeedNew(ObjectBase implementor)268 private bool NeedNew (ObjectBase implementor) 269 { 270 return elem.GetAttributeAsBoolean ("new_flag") || 271 (container_type != null && container_type.GetSignalRecursively (Name) != null) || 272 (implementor != null && implementor.GetSignalRecursively (Name) != null); 273 } 274 GenEventHandler(GenerationInfo gen_info)275 public void GenEventHandler (GenerationInfo gen_info) 276 { 277 if (IsEventHandler) 278 return; 279 280 string ns = container_type.NS; 281 282 StreamWriter sw = gen_info.OpenStream (EventHandlerName, container_type.NS); 283 284 sw.WriteLine ("namespace " + ns + " {"); 285 sw.WriteLine (); 286 sw.WriteLine ("\tusing System;"); 287 288 sw.WriteLine (); 289 sw.WriteLine ("\tpublic delegate void " + EventHandlerName + "(object o, " + EventArgsName + " args);"); 290 sw.WriteLine (); 291 sw.WriteLine ("\tpublic class " + EventArgsName + " : GLib.SignalArgs {"); 292 for (int i = 0; i < parms.Count; i++) { 293 sw.WriteLine ("\t\tpublic " + parms[i].CSType + " " + parms[i].StudlyName + "{"); 294 if (parms[i].PassAs != "out") { 295 sw.WriteLine ("\t\t\tget {"); 296 if (SymbolTable.Table.IsInterface (parms [i].CType)) { 297 var igen = SymbolTable.Table.GetInterfaceGen (parms [i].CType); 298 sw.WriteLine ("\t\t\t\treturn {0}.GetObject (Args [{1}] as GLib.Object);", igen.QualifiedAdapterName, i); 299 } else { 300 sw.WriteLine ("\t\t\t\treturn ({0}) Args [{1}];", parms [i].CSType, i); 301 } 302 sw.WriteLine ("\t\t\t}"); 303 } 304 if (parms[i].PassAs != "") { 305 sw.WriteLine ("\t\t\tset {"); 306 if (SymbolTable.Table.IsInterface (parms [i].CType)) { 307 var igen = SymbolTable.Table.GetInterfaceGen (parms [i].CType); 308 sw.WriteLine ("\t\t\t\tArgs [{0}] = value is {1} ? (value as {1}).Implementor : value;", i, igen.AdapterName); 309 } else { 310 sw.WriteLine ("\t\t\t\tArgs[" + i + "] = (" + parms [i].CSType + ")value;"); 311 } 312 sw.WriteLine ("\t\t\t}"); 313 } 314 sw.WriteLine ("\t\t}"); 315 sw.WriteLine (); 316 } 317 sw.WriteLine ("\t}"); 318 sw.WriteLine ("}"); 319 sw.Close (); 320 } 321 GenEvent(StreamWriter sw, ObjectBase implementor, string target)322 public void GenEvent (StreamWriter sw, ObjectBase implementor, string target) 323 { 324 string args_type = IsEventHandler ? "" : ", typeof (" + EventArgsQualifiedName + ")"; 325 326 if (Marshaled) { 327 GenCallback (sw); 328 args_type = ", new " + DelegateName + "(" + CallbackName + ")"; 329 } 330 331 sw.WriteLine("\t\t[GLib.Signal("+ CName + ")]"); 332 sw.Write("\t\tpublic "); 333 if (NeedNew (implementor)) 334 sw.Write("new "); 335 sw.WriteLine("event " + EventHandlerQualifiedName + " " + Name + " {"); 336 sw.WriteLine("\t\t\tadd {"); 337 sw.WriteLine("\t\t\t\t{0}.AddSignalHandler ({1}, value{2});", target, CName, args_type); 338 sw.WriteLine("\t\t\t}"); 339 sw.WriteLine("\t\t\tremove {"); 340 sw.WriteLine("\t\t\t\t{0}.RemoveSignalHandler ({1}, value);", target, CName); 341 sw.WriteLine("\t\t\t}"); 342 sw.WriteLine("\t\t}"); 343 sw.WriteLine(); 344 } 345 Generate(GenerationInfo gen_info, ObjectBase implementor)346 public void Generate (GenerationInfo gen_info, ObjectBase implementor) 347 { 348 StreamWriter sw = gen_info.Writer; 349 350 if (implementor == null) 351 GenEventHandler (gen_info); 352 353 GenEvent (sw, implementor, "this"); 354 355 Statistics.SignalCount++; 356 } 357 } 358 } 359 360