summaryrefslogtreecommitdiffstats
path: root/mono/Connection.cs
blob: f0d34eec24cc06b43890561cc86031b42d05d07b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
namespace DBus {
  
  using System;
  using System.Runtime.InteropServices;
  using System.Diagnostics;
  
  public class Connection {

    public Connection (string address) {
      // the assignment bumps the refcount
      Error error = new Error ();
      error.Init ();
      raw = dbus_connection_open (address, ref error);
      if (raw != (IntPtr) 0) {
        dbus_connection_unref (raw);
      } else {
        Exception e = new Exception (ref error);
        error.Free ();
        throw e;
      }
    }

    public static Connection Wrap (IntPtr ptr) {
      IntPtr gch_ptr;
      
      gch_ptr = dbus_connection_get_data (ptr, wrapper_slot);
      if (gch_ptr != (IntPtr) 0) {
        return (DBus.Connection) ((GCHandle)gch_ptr).Target;
      } else {
        return new Connection (ptr);
      }
    }

    // surely there's a convention for this pattern with the property
    // and the real member
    IntPtr raw_;
    IntPtr raw {
      get {
        return raw_; 
      }
      set {
        if (value == raw_)
          return;
        
        if (raw_ != (IntPtr) 0) {
          IntPtr gch_ptr;
          
          gch_ptr = dbus_connection_get_data (raw_,
                                              wrapper_slot);
          Debug.Assert (gch_ptr != (IntPtr) 0);

          dbus_connection_set_data (raw_, wrapper_slot,
                                    (IntPtr) 0, (IntPtr) 0);
          
          ((GCHandle) gch_ptr).Free ();
          
          dbus_connection_unref (raw_);
        }
        
        raw_ = value;

        if (raw_ != (IntPtr) 0) {
          GCHandle gch;

          dbus_connection_ref (raw_);

          // We store a weak reference to the C# object on the C object
          gch = GCHandle.Alloc (this, GCHandleType.WeakTrackResurrection);
          
          dbus_connection_set_data (raw_, wrapper_slot,
                                    (IntPtr) gch, (IntPtr) 0);
        }
      }
    }

    ~Connection () {
      raw = (IntPtr) 0; // free the native object
    }
    
    Connection (IntPtr r) {
      raw = r;
    }
    
    // static constructor runs before any methods 
    static Connection () {
      Debug.Assert (wrapper_slot == -1);
      
      if (!dbus_connection_allocate_data_slot (ref wrapper_slot))
        throw new OutOfMemoryException ();

      Debug.Assert (wrapper_slot >= 0);
    }

    // slot used to store the C# object on the C object
    static int wrapper_slot = -1;
    
    [DllImport (DBus.Internals.Libname, EntryPoint="dbus_connection_open")]
      private extern static IntPtr dbus_connection_open (string address,
                                                         ref Error error);

    [DllImport (DBus.Internals.Libname, EntryPoint="dbus_connection_unref")]
      private extern static void dbus_connection_unref (IntPtr ptr);

    [DllImport (DBus.Internals.Libname, EntryPoint="dbus_connection_ref")]
      private extern static void dbus_connection_ref (IntPtr ptr);

    [DllImport (DBus.Internals.Libname, EntryPoint="dbus_connection_allocate_data_slot")]
      private extern static bool dbus_connection_allocate_data_slot (ref int slot);

    [DllImport (DBus.Internals.Libname, EntryPoint="dbus_connection_free_data_slot")]
      private extern static void dbus_connection_free_data_slot (ref int slot);

    [DllImport (DBus.Internals.Libname, EntryPoint="dbus_connection_set_data")]
      private extern static bool dbus_connection_set_data (IntPtr ptr,
                                                           int    slot,
                                                           IntPtr data,
                                                           IntPtr free_data_func);

    [DllImport (DBus.Internals.Libname, EntryPoint="dbus_connection_get_data")]
      private extern static IntPtr dbus_connection_get_data (IntPtr ptr,
                                                             int    slot);
  }
}