Added signal support.
authorOwen Fraser-Green <owen@discobabe.net>
Fri, 26 Mar 2004 15:25:59 +0000 (15:25 +0000)
committerOwen Fraser-Green <owen@discobabe.net>
Fri, 26 Mar 2004 15:25:59 +0000 (15:25 +0000)
mono/Connection.cs
mono/ErrorMessage.cs [new file with mode: 0644]
mono/Handler.cs
mono/InterfaceProxy.cs
mono/Makefile.am
mono/Message.cs
mono/ProxyBuilder.cs
mono/Service.cs
mono/Signal.cs
mono/SignalAttribute.cs [new file with mode: 0644]

index 406e779..5e25ffd 100644 (file)
@@ -42,6 +42,11 @@ namespace DBus
       SetupWithMain();
     }
 
+    public void Flush()
+    {
+      dbus_connection_flush(RawConnection);
+    }
+
     public void SetupWithMain() 
     {      
       dbus_connection_setup_with_g_main(RawConnection, IntPtr.Zero);
diff --git a/mono/ErrorMessage.cs b/mono/ErrorMessage.cs
new file mode 100644 (file)
index 0000000..773a05b
--- /dev/null
@@ -0,0 +1,45 @@
+namespace DBus
+{
+  using System;
+  using System.Runtime.InteropServices;
+  using System.Diagnostics;
+  
+  public class ErrorMessage : Message
+  {    
+    public ErrorMessage() : base(MessageType.Error)
+    {  
+    }
+
+    internal ErrorMessage(IntPtr rawMessage, Service service) : base(rawMessage, service)
+    {
+    }
+
+    public ErrorMessage(Service service) : base(MessageType.Error, service) 
+    {
+    }
+
+    public new string Name
+    {
+      get {
+       if (this.name == null) {
+         this.name = Marshal.PtrToStringAnsi(dbus_message_get_error_name(RawMessage));
+       }
+       
+       return this.name;
+      }
+      
+      set {
+       if (value != this.name) {
+         dbus_message_set_error_name(RawMessage, value);
+         this.name = value;
+       }
+      }
+    }
+
+    [DllImport("dbus-1")]
+    private extern static bool dbus_message_set_error_name(IntPtr rawMessage, string name);
+
+    [DllImport("dbus-1")]
+    private extern static IntPtr dbus_message_get_error_name(IntPtr rawMessage);
+  }
+}
index a854c9c..be03dc5 100644 (file)
@@ -6,6 +6,13 @@ namespace DBus
   using System.Reflection;
   using System.Collections;
 
+  internal enum Result 
+  {
+    Handled = 0,
+    NotYetHandled = 1,
+    NeedMemory = 2
+  }
+
   internal class Handler
   {
     private string[] path = null;
@@ -15,7 +22,6 @@ namespace DBus
     private DBusObjectPathVTable vTable;
     private Connection connection;
     private Service service;
-    private DBusHandleMessageFunction filterCalled;
     
     internal delegate void DBusObjectPathUnregisterFunction(IntPtr rawConnection,
                                                            IntPtr userData);
@@ -24,18 +30,6 @@ namespace DBus
                                                        IntPtr rawMessage,
                                                        IntPtr userData);
 
-    internal delegate int DBusHandleMessageFunction(IntPtr rawConnection,
-                                                   IntPtr rawMessage,
-                                                   IntPtr userData);
-
-
-    private enum Result 
-    {
-      Handled = 0,
-      NotYetHandled = 1,
-      NeedMemory = 2
-    }
-
     [StructLayout (LayoutKind.Sequential)]
     private struct DBusObjectPathVTable 
     {
@@ -67,8 +61,8 @@ namespace DBus
     }
 
     public Handler(object handledObject, 
-                      string pathName, 
-                      Service service)
+                  string pathName, 
+                  Service service)
     {
       Service = service;
       Connection = service.Connection;
@@ -88,14 +82,22 @@ namespace DBus
                                                ref vTable,
                                                IntPtr.Zero))
        throw new OutOfMemoryException();
-      
-      // Setup the filter function
-      this.filterCalled = new DBusHandleMessageFunction(Filter_Called);
-      if (!dbus_connection_add_filter(Connection.RawConnection,
-                                     this.filterCalled,
-                                     IntPtr.Zero,
-                                     IntPtr.Zero))
-       throw new OutOfMemoryException();
+
+      RegisterSignalHandlers();
+    }
+
+    private void RegisterSignalHandlers()
+    {
+      ProxyBuilder proxyBuilder = new ProxyBuilder(Service, HandledObject.GetType(), this.pathName);
+
+      foreach (DictionaryEntry interfaceEntry in this.introspector.InterfaceProxies) {
+       InterfaceProxy interfaceProxy = (InterfaceProxy) interfaceEntry.Value;
+       foreach (DictionaryEntry signalEntry in interfaceProxy.Signals) {
+         EventInfo eventE = (EventInfo) signalEntry.Value;
+         Delegate del = Delegate.CreateDelegate(eventE.EventHandlerType, proxyBuilder.GetSignalProxy(), "Proxy_" + eventE.Name);
+         eventE.AddEventHandler(HandledObject, del);
+       }
+      }
     }
 
     public object HandledObject 
@@ -113,21 +115,6 @@ namespace DBus
        this.introspector = Introspector.GetIntrospector(value.GetType());        
       }
     }
-    
-    public int Filter_Called(IntPtr rawConnection,
-                            IntPtr rawMessage,
-                            IntPtr userData) 
-    {
-      Message message = Message.Wrap(rawMessage, Service);
-      
-      if (message.Type == Message.MessageType.Signal) {
-       Signal signal = (Signal) message;
-      } else if (message.Type == Message.MessageType.MethodCall) {
-       MethodCall methodCall = (MethodCall) message;
-      }
-      
-      return (int) Result.NotYetHandled;
-    }
 
     public void Unregister_Called(IntPtr rawConnection, 
                                  IntPtr userData)
@@ -143,7 +130,8 @@ namespace DBus
 
       switch (message.Type) {
       case Message.MessageType.Signal:
-       System.Console.WriteLine("FIXME: Signal called.");
+       // We're not interested in signals here because we're the ones
+       // that generate them!
        break;
       case Message.MessageType.MethodCall:
        return (int) HandleMethod((MethodCall) message);
@@ -216,7 +204,7 @@ namespace DBus
        {
          this.service = value;
        }
-    }    
+    }
 
     [DllImport ("dbus-1")]
     private extern static bool dbus_connection_register_object_path (IntPtr rawConnection, string[] path, ref DBusObjectPathVTable vTable, IntPtr userData);
@@ -224,11 +212,5 @@ namespace DBus
     [DllImport ("dbus-1")]
     private extern static void dbus_connection_unregister_object_path (IntPtr rawConnection, string[] path);
 
-    [DllImport ("dbus-1")]
-    private extern static bool dbus_connection_add_filter(IntPtr rawConnection,
-                                                         DBusHandleMessageFunction filter,
-                                                         IntPtr userData,
-                                                         IntPtr freeData);
-
   }
 }
index 6ccc963..5069773 100644 (file)
@@ -8,7 +8,8 @@ namespace DBus
   {
     private static Hashtable interfaceProxies = new Hashtable();
     private Hashtable methods = null;
-
+    private Hashtable signals = null;
+    
     private string interfaceName;
 
     private InterfaceProxy(Type type) 
@@ -17,17 +18,34 @@ namespace DBus
       InterfaceAttribute interfaceAttribute = (InterfaceAttribute) attributes[0];
       this.interfaceName = interfaceAttribute.InterfaceName;
       AddMethods(type);
+      AddSignals(type);
     }
 
+    // Add all the events with Signal attributes
+    private void AddSignals(Type type)
+    {
+      this.signals = new Hashtable();
+      foreach (EventInfo signal in type.GetEvents(BindingFlags.Public |
+                                                 BindingFlags.Instance |
+                                                 BindingFlags.DeclaredOnly)) {
+       object[] attributes = signal.GetCustomAttributes(typeof(SignalAttribute), false);
+       if (attributes.GetLength(0) > 0) {
+         MethodInfo invoke = signal.EventHandlerType.GetMethod("Invoke");
+         signals.Add(signal.Name + " " + GetSignature(invoke), signal);
+       }
+      }      
+    }
+
+    // Add all the methods with Method attributes
     private void AddMethods(Type type)
     {
       this.methods = new Hashtable();
       foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | 
                                                    BindingFlags.Instance | 
                                                    BindingFlags.DeclaredOnly)) {
-       object[] attributes = method.GetCustomAttributes(typeof(MethodAttribute), true);
+       object[] attributes = method.GetCustomAttributes(typeof(MethodAttribute), false);
        if (attributes.GetLength(0) > 0) {
-         methods.Add(GetKey(method), method);
+         methods.Add(method.Name + " " + GetSignature(method), method);
        }
       }
     }
@@ -46,16 +64,26 @@ namespace DBus
     {
       return this.Methods.Contains(key);
     }
+
+    public bool HasSignal(string key)
+    {
+      return this.Signals.Contains(key);
+    }
+    
+    public EventInfo GetSignal(string key)
+    {
+      return (EventInfo) this.Signals[key];
+    }
     
     public MethodInfo GetMethod(string key)
     {
       return (MethodInfo) this.Methods[key];
     }
 
-    private string GetKey(MethodInfo method) 
+    public static string GetSignature(MethodInfo method) 
     {
       ParameterInfo[] pars = method.GetParameters();
-      string key = method.Name + " ";
+      string key = "";
       
       foreach (ParameterInfo par in pars) {
        if (!par.IsOut) {
@@ -73,6 +101,13 @@ namespace DBus
        return this.methods;
       }
     }
+
+    public Hashtable Signals
+    {
+      get {
+       return this.signals;
+      }
+    }
     
     public string InterfaceName
     {
index b35a5c4..6efd771 100644 (file)
@@ -10,6 +10,7 @@ DBUS_SHARP_FILES=             \
        Custom.cs               \
        DBusException.cs        \
        Error.cs                \
+       ErrorMessage.cs         \
        Handler.cs              \
        InterfaceAttribute.cs   \
        InterfaceProxy.cs       \
@@ -22,6 +23,7 @@ DBUS_SHARP_FILES=             \
        Server.cs               \
        Service.cs              \
        Signal.cs               \
+       SignalAttribute.cs      \
        DBusType/IDBusType.cs   \
        DBusType/Array.cs       \
        DBusType/Boolean.cs     \
index a1ff220..54472d1 100644 (file)
@@ -80,14 +80,23 @@ namespace DBus
       } 
       // If it doesn't exist then create a new Message around it
       Message message = null;
+      MessageType messageType = (MessageType) dbus_message_get_type(rawMessage);
       
-      switch ((MessageType) dbus_message_get_type(rawMessage)) {
+      switch (messageType) {
       case MessageType.Signal:
        message = new Signal(rawMessage, service);
        break;
       case MessageType.MethodCall:
        message = new MethodCall(rawMessage, service);
        break;
+      case MessageType.MethodReturn:
+       message = new MethodReturn(rawMessage, service);
+       break;
+      case MessageType.Error:
+       message = new ErrorMessage(rawMessage, service);
+       break;
+      default:
+       throw new ApplicationException("Unknown message type to wrap: " + messageType);
       }
 
       return message;
@@ -140,6 +149,8 @@ namespace DBus
     {
       if (!dbus_connection_send (Service.Connection.RawConnection, RawMessage, ref serial))
        throw new OutOfMemoryException ();
+
+      Service.Connection.Flush();
     }
     
     public void Send() 
@@ -245,23 +256,19 @@ namespace DBus
     
     protected virtual string Name
     {
-      set 
-       {
-         if (value != this.name)
-           {
-             dbus_message_set_member (RawMessage, value);
-             this.name = value;
-           }
+      set {
+       if (value != this.name) {
+         dbus_message_set_member(RawMessage, value);
+         this.name = value;
        }
-      get 
-       {
-         if (this.name == null) {
-           this.name = Marshal.PtrToStringAnsi(dbus_message_get_member(RawMessage));
-         }
-
-
-         return this.name;
+      }
+      get {
+       if (this.name == null) {
+         this.name = Marshal.PtrToStringAnsi(dbus_message_get_member(RawMessage));
        }
+
+       return this.name;
+      }
     }
 
     public string Key
@@ -356,7 +363,7 @@ namespace DBus
     private extern static IntPtr dbus_message_get_interface(IntPtr rawMessage);
     
     [DllImport("dbus-1")]
-    private extern static bool dbus_message_set_member (IntPtr rawMessage, string name);
+    private extern static bool dbus_message_set_member(IntPtr rawMessage, string name);
 
     [DllImport("dbus-1")]
     private extern static IntPtr dbus_message_get_member(IntPtr rawMessage);
index 4ef2fe9..8044909 100644 (file)
@@ -14,20 +14,27 @@ namespace DBus
     private string pathName = null;
     private Type type = null;
     private Introspector introspector = null;
-    private static AssemblyBuilder proxyAssembly;
     
     private static MethodInfo Service_NameMI = typeof(Service).GetMethod("get_Name", 
                                                                            new Type[0]);
     private static MethodInfo Service_ConnectionMI = typeof(Service).GetMethod("get_Connection",
                                                                                  new Type[0]);
+    private static MethodInfo Service_AddSignalCalledMI = typeof(Service).GetMethod("add_SignalCalled",
+                                                                                   new Type[] {typeof(Service.SignalCalledHandler)});
+    private static MethodInfo Signal_PathNameMI = typeof(Signal).GetMethod("get_PathName",
+                                                                          new Type[0]);
     private static MethodInfo Message_ArgumentsMI = typeof(Message).GetMethod("get_Arguments",
                                                                                 new Type[0]);
+    private static MethodInfo Message_KeyMI = typeof(Message).GetMethod("get_Key",
+                                                                       new Type[0]);
     private static MethodInfo Arguments_InitAppendingMI = typeof(Arguments).GetMethod("InitAppending",
                                                                                          new Type[0]);
     private static MethodInfo Arguments_AppendMI = typeof(Arguments).GetMethod("Append",
                                                                                  new Type[] {typeof(DBusType.IDBusType)});
     private static MethodInfo Message_SendWithReplyAndBlockMI = typeof(Message).GetMethod("SendWithReplyAndBlock",
                                                                                             new Type[0]);
+    private static MethodInfo Message_SendMI = typeof(Message).GetMethod("Send",
+                                                                        new Type[0]);
     private static MethodInfo Arguments_GetEnumeratorMI = typeof(Arguments).GetMethod("GetEnumerator",
                                                                                          new Type[0]);
     private static MethodInfo IEnumerator_MoveNextMI = typeof(System.Collections.IEnumerator).GetMethod("MoveNext",
@@ -42,8 +49,19 @@ namespace DBus
                                                                                                typeof(string),
                                                                                                typeof(string),
                                                                                                typeof(string)});
+    private static ConstructorInfo Signal_C = typeof(Signal).GetConstructor(new Type[] {typeof(Service),
+                                                                                       typeof(string),
+                                                                                       typeof(string),
+                                                                                       typeof(string)});
+    private static ConstructorInfo Service_SignalCalledHandlerC = typeof(Service.SignalCalledHandler).GetConstructor(new Type[] {typeof(object),
+                                                                                                                                typeof(System.IntPtr)});
+    private static MethodInfo String_opEqualityMI = typeof(System.String).GetMethod("op_Equality",
+                                                                                   new Type[] {typeof(string),
+                                                                                               typeof(string)});                                                                                                            
+    private static MethodInfo MulticastDelegate_opInequalityMI = typeof(System.MulticastDelegate).GetMethod("op_Inequality",
+                                                                                   new Type[] {typeof(System.MulticastDelegate),
+                                                                                               typeof(System.MulticastDelegate)});
     
-                                                                                       
 
     public ProxyBuilder(Service service, Type type, string pathName)
     {
@@ -53,6 +71,163 @@ namespace DBus
       this.introspector = Introspector.GetIntrospector(type);
     }
 
+    private MethodInfo BuildSignalCalled(ref TypeBuilder typeB, FieldInfo serviceF, FieldInfo pathF)
+    {
+      Type[] parTypes = {typeof(Signal)};
+      MethodBuilder methodBuilder = typeB.DefineMethod("Service_SignalCalled",
+                                                      MethodAttributes.Private |
+                                                      MethodAttributes.HideBySig,
+                                                      typeof(void),
+                                                      parTypes);
+      
+      ILGenerator generator = methodBuilder.GetILGenerator();
+
+      LocalBuilder enumeratorL = generator.DeclareLocal(typeof(System.Collections.IEnumerator));
+      enumeratorL.SetLocalSymInfo("enumerator");
+
+      Label wrongPath = generator.DefineLabel();
+      //generator.EmitWriteLine("if (signal.PathName == pathName) {");
+      generator.Emit(OpCodes.Ldarg_1);
+      generator.EmitCall(OpCodes.Callvirt, Signal_PathNameMI, null);
+      generator.Emit(OpCodes.Ldarg_0);
+      generator.Emit(OpCodes.Ldfld, pathF);
+      generator.EmitCall(OpCodes.Call, String_opEqualityMI, null);
+      generator.Emit(OpCodes.Brfalse, wrongPath);
+
+      int localOffset = 1;
+
+      foreach (DictionaryEntry interfaceEntry in this.introspector.InterfaceProxies) {
+       InterfaceProxy interfaceProxy = (InterfaceProxy) interfaceEntry.Value;
+       foreach (DictionaryEntry signalEntry in interfaceProxy.Signals) {
+         EventInfo eventE = (EventInfo) signalEntry.Value;
+         // This is really cheeky since we need to grab the event as a private field.
+         FieldInfo eventF = this.type.GetField(eventE.Name,
+                                               BindingFlags.NonPublic|
+                                               BindingFlags.Instance);
+
+         MethodInfo eventHandler_InvokeMI = eventE.EventHandlerType.GetMethod("Invoke");
+
+         ParameterInfo[] pars = eventHandler_InvokeMI.GetParameters();
+         parTypes = new Type[pars.Length];
+         for (int parN = 0; parN < pars.Length; parN++) {
+           parTypes[parN] = pars[parN].ParameterType;
+           LocalBuilder parmL = generator.DeclareLocal(parTypes[parN]);
+           parmL.SetLocalSymInfo(pars[parN].Name);
+         }
+         
+         Label skip = generator.DefineLabel();      
+         //generator.EmitWriteLine("  if (SelectedIndexChanged != null) {");
+         generator.Emit(OpCodes.Ldarg_0);
+         generator.Emit(OpCodes.Ldfld, eventF);
+         generator.Emit(OpCodes.Ldnull);
+         generator.EmitCall(OpCodes.Call, MulticastDelegate_opInequalityMI, null);
+         generator.Emit(OpCodes.Brfalse, skip);
+         
+         //generator.EmitWriteLine("    if (signal.Key == 'la i')");
+         generator.Emit(OpCodes.Ldarg_1);
+         generator.EmitCall(OpCodes.Callvirt, Message_KeyMI, null);
+         generator.Emit(OpCodes.Ldstr, eventE.Name + " " + InterfaceProxy.GetSignature(eventHandler_InvokeMI));
+         generator.EmitCall(OpCodes.Call, String_opEqualityMI, null);
+         generator.Emit(OpCodes.Brfalse, skip);
+
+         //generator.EmitWriteLine("IEnumerator enumerator = signal.Arguments.GetEnumerator()");
+         generator.Emit(OpCodes.Ldarg_1);
+         generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null);
+         generator.EmitCall(OpCodes.Callvirt, Arguments_GetEnumeratorMI, null);
+         generator.Emit(OpCodes.Stloc_0);
+         
+         for (int parN = 0; parN < pars.Length; parN++) {
+           ParameterInfo par = pars[parN];
+           if (!par.IsOut) {
+             EmitSignalIn(generator, par.ParameterType, parN + localOffset, serviceF);
+           }
+         }
+         
+         //generator.EmitWriteLine("    SelectedIndexChanged(selectedIndex)");
+         generator.Emit(OpCodes.Ldarg_0);
+         generator.Emit(OpCodes.Ldfld, eventF);
+         for (int parN = 0; parN < pars.Length; parN++) {
+           generator.Emit(OpCodes.Ldloc_S, parN + localOffset);
+         }
+         
+         generator.EmitCall(OpCodes.Callvirt, eventHandler_InvokeMI, null);
+         
+         generator.MarkLabel(skip);
+         //generator.EmitWriteLine("  }");
+         
+         localOffset += pars.Length;
+       }
+      }
+
+      generator.MarkLabel(wrongPath);
+      //generator.EmitWriteLine("}");
+
+      //generator.EmitWriteLine("return");
+      generator.Emit(OpCodes.Ret);
+
+      return methodBuilder;
+    }
+    
+    private void BuildSignalHandler(EventInfo eventE, 
+                                   InterfaceProxy interfaceProxy,
+                                   ref TypeBuilder typeB, 
+                                   FieldInfo serviceF,
+                                   FieldInfo pathF)
+    {
+      MethodInfo eventHandler_InvokeMI = eventE.EventHandlerType.GetMethod("Invoke");
+      ParameterInfo[] pars = eventHandler_InvokeMI.GetParameters();
+      Type[] parTypes = new Type[pars.Length];
+      for (int parN = 0; parN < pars.Length; parN++) {
+       parTypes[parN] = pars[parN].ParameterType;
+      }
+
+      // Generate the code
+      MethodBuilder methodBuilder = typeB.DefineMethod("Proxy_" + eventE.Name, 
+                                                      MethodAttributes.Public |
+                                                      MethodAttributes.HideBySig |
+                                                      MethodAttributes.Virtual, 
+                                                      typeof(void),
+                                                      parTypes);
+      ILGenerator generator = methodBuilder.GetILGenerator();
+
+      for (int parN = 0; parN < pars.Length; parN++) {
+       methodBuilder.DefineParameter(parN + 1, pars[parN].Attributes, pars[parN].Name);
+      }
+
+      // Generate the locals
+      LocalBuilder methodCallL = generator.DeclareLocal(typeof(MethodCall));
+      methodCallL.SetLocalSymInfo("signal");
+      LocalBuilder replyL = generator.DeclareLocal(typeof(MethodReturn));
+
+      //generator.EmitWriteLine("Signal signal = new Signal(...)");
+      generator.Emit(OpCodes.Ldsfld, serviceF);
+      generator.Emit(OpCodes.Ldarg_0);
+      generator.Emit(OpCodes.Ldfld, pathF);
+      generator.Emit(OpCodes.Ldstr, interfaceProxy.InterfaceName);
+      generator.Emit(OpCodes.Ldstr, eventE.Name);
+      generator.Emit(OpCodes.Newobj, Signal_C);
+      generator.Emit(OpCodes.Stloc_0);
+
+      //generator.EmitWriteLine("signal.Arguments.InitAppending()");
+      generator.Emit(OpCodes.Ldloc_0);
+      generator.EmitCall(OpCodes.Callvirt, Message_ArgumentsMI, null);
+      generator.EmitCall(OpCodes.Callvirt, Arguments_InitAppendingMI, null);
+
+      for (int parN = 0; parN < pars.Length; parN++) {
+       ParameterInfo par = pars[parN];
+       if (!par.IsOut) {
+         EmitIn(generator, par.ParameterType, parN, serviceF);
+       }
+      }
+      
+      //generator.EmitWriteLine("signal.Send()");
+      generator.Emit(OpCodes.Ldloc_0);
+      generator.EmitCall(OpCodes.Callvirt, Message_SendMI, null); 
+
+      //generator.EmitWriteLine("return");
+      generator.Emit(OpCodes.Ret);
+    }
+
     private void BuildMethod(MethodInfo method, 
                             InterfaceProxy interfaceProxy,
                             ref TypeBuilder typeB, 
@@ -145,6 +320,28 @@ namespace DBus
       typeB.DefineMethodOverride(methodBuilder, method);
     }
 
+    private void EmitSignalIn(ILGenerator generator, Type parType, int parN, FieldInfo serviceF)
+    {
+       //generator.EmitWriteLine("enumerator.MoveNext()");
+       generator.Emit(OpCodes.Ldloc_0);
+       generator.EmitCall(OpCodes.Callvirt, IEnumerator_MoveNextMI, null);
+       
+       Type outParType = Arguments.MatchType(parType);
+       //generator.EmitWriteLine("int selectedIndex = (int) ((DBusType.IDBusType) enumerator.Current).Get(typeof(int))");
+       generator.Emit(OpCodes.Pop);
+       generator.Emit(OpCodes.Ldloc_0);
+       generator.EmitCall(OpCodes.Callvirt, IEnumerator_CurrentMI, null);
+       generator.Emit(OpCodes.Castclass, typeof(DBusType.IDBusType));
+       generator.Emit(OpCodes.Ldtoken, parType);
+       generator.EmitCall(OpCodes.Call, Type_GetTypeFromHandleMI, null);
+       generator.EmitCall(OpCodes.Callvirt, IDBusType_GetMI, null);
+       // Call the DBusType EmitMarshalOut to make it emit itself
+       object[] pars = new object[] {generator, parType, true};
+       outParType.InvokeMember("EmitMarshalOut", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, pars, null);
+       generator.Emit(OpCodes.Stloc_S, parN);
+    }
+    
+
     private void EmitIn(ILGenerator generator, Type parType, int parN, FieldInfo serviceF)
     {
       Type inParType = Arguments.MatchType(parType);
@@ -191,7 +388,7 @@ namespace DBus
       }
     }
     
-    public void BuildConstructor(ref TypeBuilder typeB, FieldInfo serviceF, FieldInfo pathF)
+    public void BuildConstructor(ref TypeBuilder typeB, FieldInfo serviceF, FieldInfo pathF, MethodInfo signalCalledMI)
     {
       Type[] pars = {typeof(Service), typeof(string)};
       ConstructorBuilder constructor = typeB.DefineConstructor(MethodAttributes.RTSpecialName | 
@@ -201,23 +398,102 @@ namespace DBus
       ILGenerator generator = constructor.GetILGenerator();
       generator.Emit(OpCodes.Ldarg_0);
       generator.Emit(OpCodes.Call, this.introspector.Constructor);
+      //generator.EmitWriteLine("service = myService");
       generator.Emit(OpCodes.Ldarg_1);
       generator.Emit(OpCodes.Stsfld, serviceF);
+      //generator.EmitWriteLine("this.pathName = pathName");
       generator.Emit(OpCodes.Ldarg_0);
       generator.Emit(OpCodes.Ldarg_2);
       generator.Emit(OpCodes.Stfld, pathF);
+      
+      //generator.EmitWriteLine("myService.SignalCalled += new Service.SignalCalledHandler(Service_SignalCalled)");
+      generator.Emit(OpCodes.Ldarg_1);
+      generator.Emit(OpCodes.Ldarg_0);
+      generator.Emit(OpCodes.Ldftn, signalCalledMI);
+      generator.Emit(OpCodes.Newobj, Service_SignalCalledHandlerC);
+      generator.EmitCall(OpCodes.Callvirt, Service_AddSignalCalledMI, null);
 
+      //generator.EmitWriteLine("return");
+      generator.Emit(OpCodes.Ret);
+    }
+
+    public void BuildSignalConstructor(ref TypeBuilder typeB, FieldInfo serviceF, FieldInfo pathF)
+    {
+      Type[] pars = {typeof(Service), typeof(string)};
+      ConstructorBuilder constructor = typeB.DefineConstructor(MethodAttributes.RTSpecialName | 
+                                                              MethodAttributes.Public,
+                                                              CallingConventions.Standard, pars);
+
+      ILGenerator generator = constructor.GetILGenerator();
+      generator.Emit(OpCodes.Ldarg_0);
+      generator.Emit(OpCodes.Call, this.introspector.Constructor);
+      //generator.EmitWriteLine("service = myService");
+      generator.Emit(OpCodes.Ldarg_1);
+      generator.Emit(OpCodes.Stsfld, serviceF);
+      //generator.EmitWriteLine("this.pathName = pathName");
+      generator.Emit(OpCodes.Ldarg_0);
+      generator.Emit(OpCodes.Ldarg_2);
+      generator.Emit(OpCodes.Stfld, pathF);
+      
+      //generator.EmitWriteLine("return");
       generator.Emit(OpCodes.Ret);
     }
     
-    public object GetProxy() 
-    {      
+    public object GetSignalProxy()
+    {
+      Type proxyType = Service.ProxyAssembly.GetType(ObjectName + ".SignalProxy");
+
+      if (proxyType == null) {
+       // Build the type
+       TypeBuilder typeB = Service.Module.DefineType(ObjectName + ".SignalProxy", 
+                                                     TypeAttributes.Public, 
+                                                     this.type);
+       
+       FieldBuilder serviceF = typeB.DefineField("service", 
+                                                 typeof(Service), 
+                                                 FieldAttributes.Private | 
+                                                 FieldAttributes.Static);
+       FieldBuilder pathF = typeB.DefineField("pathName", 
+                                              typeof(string), 
+                                              FieldAttributes.Private);
+
+       BuildSignalConstructor(ref typeB, serviceF, pathF);
+       
+       // Build the signal handlers
+       foreach (DictionaryEntry interfaceEntry in this.introspector.InterfaceProxies) {
+         InterfaceProxy interfaceProxy = (InterfaceProxy) interfaceEntry.Value;
+         foreach (DictionaryEntry signalEntry in interfaceProxy.Signals) {
+           EventInfo eventE = (EventInfo) signalEntry.Value;
+           BuildSignalHandler(eventE, interfaceProxy, ref typeB, serviceF, pathF);
+         }
+       }
+       
+       proxyType = typeB.CreateType();
       
-      Type proxyType = ProxyAssembly.GetType(ProxyName);
+       // Uncomment the following line to produce a DLL of the
+       // constructed assembly which can then be examined using
+       // monodis. Note that in order for this to work you should copy
+       // the client assembly as a dll file so that monodis can pick it
+       // up.
+       //Service.ProxyAssembly.Save("proxy.dll");
+      }
+
+      Type [] parTypes = new Type[] {typeof(Service), typeof(string)};
+      object [] pars = new object[] {Service, pathName};
+      
+      ConstructorInfo constructor = proxyType.GetConstructor(parTypes);
+      object instance = constructor.Invoke(pars);
+      return instance;
+    }
+      
+    
+    public object GetProxy() 
+    { 
+      Type proxyType = Service.ProxyAssembly.GetType(ObjectName + ".Proxy");
       
       if (proxyType == null) {
        // Build the type
-       TypeBuilder typeB = ServiceModuleBuilder.DefineType(ProxyName, TypeAttributes.Public, this.type);
+       TypeBuilder typeB = Service.Module.DefineType(ObjectName + ".Proxy", TypeAttributes.Public, this.type);
        
        FieldBuilder serviceF = typeB.DefineField("service", 
                                                  typeof(Service), 
@@ -227,7 +503,8 @@ namespace DBus
                                               typeof(string), 
                                               FieldAttributes.Private);
        
-       BuildConstructor(ref typeB, serviceF, pathF);
+       MethodInfo signalCalledMI = BuildSignalCalled(ref typeB, serviceF, pathF);
+       BuildConstructor(ref typeB, serviceF, pathF, signalCalledMI);
        
        // Build the methods
        foreach (DictionaryEntry interfaceEntry in this.introspector.InterfaceProxies) {
@@ -245,7 +522,7 @@ namespace DBus
        // monodis. Note that in order for this to work you should copy
        // the client assembly as a dll file so that monodis can pick it
        // up.
-       //ProxyAssembly.Save("proxy.dll");
+       //Service.ProxyAssembly.Save("proxy.dll");
       }
 
       Type [] parTypes = new Type[] {typeof(Service), typeof(string)};
@@ -255,43 +532,18 @@ namespace DBus
       object instance = constructor.Invoke(pars);
       return instance;
     }
-
-    private ModuleBuilder ServiceModuleBuilder
-    {
-      get {
-       if (Service.module == null) {
-         Service.module = ProxyAssembly.DefineDynamicModule(Service.Name, "proxy.dll", true);
-       }
-       
-       return Service.module;
-      }
-    }
-  
-  private Service Service
+    
+    private Service Service
     {
       get {
        return this.service;
       }
     }
 
-    private string ProxyName
+    private string ObjectName
     {
       get {
-       return this.introspector.ToString() + ".Proxy";
-      }
-    }
-
-    private AssemblyBuilder ProxyAssembly
-    {
-      get {
-       if (proxyAssembly == null){
-         AssemblyName assemblyName = new AssemblyName();
-         assemblyName.Name = "DBusProxy";
-         proxyAssembly = Thread.GetDomain().DefineDynamicAssembly(assemblyName, 
-                                                                  AssemblyBuilderAccess.RunAndSave);
-       }
-       
-       return proxyAssembly;
+       return this.introspector.ToString();
       }
     }
   }
index 16e52d1..c34ba3d 100644 (file)
@@ -4,6 +4,7 @@ namespace DBus
   using System.Runtime.InteropServices;
   using System.Diagnostics;
   using System.Collections;
+  using System.Threading;
   using System.Reflection;
   using System.Reflection.Emit;
   
@@ -13,12 +14,20 @@ namespace DBus
     private string name;
     private bool local = false;
     private Hashtable registeredHandlers = new Hashtable();
-    internal ModuleBuilder module = null;
+    private delegate int DBusHandleMessageFunction(IntPtr rawConnection,
+                                                  IntPtr rawMessage,
+                                                  IntPtr userData);
+    private DBusHandleMessageFunction filterCalled;
+    public delegate void SignalCalledHandler(Signal signal);
+    public event SignalCalledHandler SignalCalled;
+    private static AssemblyBuilder proxyAssembly;
+    private ModuleBuilder module = null;
 
     internal Service(string name, Connection connection)
     {
       this.name = name;
       this.connection = connection;
+      AddFilter();
     }
 
     public Service(Connection connection, string name)
@@ -67,9 +76,7 @@ namespace DBus
     public void RegisterObject(object handledObject, 
                               string pathName) 
     {
-      Handler handler = new Handler(handledObject, 
-                                   pathName, 
-                                   this);
+      Handler handler = new Handler(handledObject, pathName, this);
       registeredHandlers.Add(handledObject, handler);
     }
 
@@ -89,6 +96,38 @@ namespace DBus
       return proxy;
     }
 
+    private void AddFilter() 
+    {
+      // Setup the filter function
+      this.filterCalled = new DBusHandleMessageFunction(Service_FilterCalled);
+      if (!dbus_connection_add_filter(Connection.RawConnection,
+                                     this.filterCalled,
+                                     IntPtr.Zero,
+                                     IntPtr.Zero))
+       throw new OutOfMemoryException();
+
+      // Add a match for signals. FIXME: Can we filter the service?
+      string rule = "type='signal'";
+      dbus_bus_add_match(connection.RawConnection, rule, IntPtr.Zero);
+    }
+
+    private int Service_FilterCalled(IntPtr rawConnection,
+                                   IntPtr rawMessage,
+                                   IntPtr userData) 
+    {
+      Message message = Message.Wrap(rawMessage, this);
+      
+      if (message.Type == Message.MessageType.Signal) {
+       // We're only interested in signals
+       Signal signal = (Signal) message;
+       if (SignalCalled != null) {
+         SignalCalled(signal);
+       }
+      }
+      
+      return (int) Result.NotYetHandled;
+    }
+
     public string Name
     {
       get
@@ -110,10 +149,51 @@ namespace DBus
        }
     }
 
-    [DllImport ("dbus-1")]
-    private extern static int dbus_bus_acquire_service (IntPtr rawConnection, string serviceName, uint flags, ref Error error);
+    internal AssemblyBuilder ProxyAssembly
+    {
+      get {
+       if (proxyAssembly == null){
+         AssemblyName assemblyName = new AssemblyName();
+         assemblyName.Name = "DBusProxy";
+         proxyAssembly = Thread.GetDomain().DefineDynamicAssembly(assemblyName, 
+                                                                  AssemblyBuilderAccess.RunAndSave);
+       }
+       
+       return proxyAssembly;
+      }
+    }
+
+    internal ModuleBuilder Module
+    {
+      get {
+       if (this.module == null) {
+         this.module = ProxyAssembly.DefineDynamicModule(Name, "proxy.dll", true);
+       }
+       
+       return this.module;
+      }
+    }
+
+    [DllImport("dbus-1")]
+    private extern static int dbus_bus_acquire_service(IntPtr rawConnection, 
+                                                       string serviceName, 
+                                                       uint flags, ref Error error);
+
+    [DllImport("dbus-1")]
+    private extern static bool dbus_bus_service_exists(IntPtr rawConnection, 
+                                                      string serviceName, 
+                                                      ref Error error);    
+
+    [DllImport("dbus-1")]
+    private extern static bool dbus_connection_add_filter(IntPtr rawConnection,
+                                                         DBusHandleMessageFunction filter,
+                                                         IntPtr userData,
+                                                         IntPtr freeData);
+
+    [DllImport("dbus-1")]
+    private extern static void dbus_bus_add_match(IntPtr rawConnection,
+                                                 string rule,
+                                                 IntPtr erro);
 
-    [DllImport ("dbus-1")]
-    private extern static bool dbus_bus_service_exists (IntPtr rawConnection, string serviceName, ref Error error);    
   }
 }
index a9209fe..10191f9 100644 (file)
@@ -18,6 +18,23 @@ namespace DBus
     {
     }
 
+    public Signal(Service service, string pathName, string interfaceName, string name)
+    {
+      this.service = service;
+
+      RawMessage = dbus_message_new_signal(pathName, interfaceName, name);
+      
+      if (RawMessage == IntPtr.Zero) {
+       throw new OutOfMemoryException();
+      }
+      
+      this.pathName = pathName;
+      this.interfaceName = interfaceName;
+      this.name = name;
+
+      dbus_message_unref(RawMessage);
+    }
+
     public new string PathName
     {
       get
@@ -56,5 +73,7 @@ namespace DBus
          base.Name = value;
        }
     }
+    [DllImport("dbus-1")]
+    private extern static IntPtr dbus_message_new_signal(string pathName, string interfaceName, string name);
   }
 }
diff --git a/mono/SignalAttribute.cs b/mono/SignalAttribute.cs
new file mode 100644 (file)
index 0000000..4835444
--- /dev/null
@@ -0,0 +1,11 @@
+using System;
+
+namespace DBus
+{
+  [AttributeUsage(AttributeTargets.Event, AllowMultiple=false, Inherited=true)]  public class SignalAttribute : Attribute
+  {
+    public SignalAttribute()
+    {
+    }
+  }
+}