summaryrefslogtreecommitdiff
path: root/mono/Message.cs
blob: 8f6db007183fa1f0203a97f11fc155f664b157ce (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
124
125
126
127
128
129
namespace DBus {
  
  using System;
  using System.Runtime.InteropServices;
  using System.Diagnostics;
  
  public class Message {

    public Message (string name,
                    string dest_service) {
      // the assignment bumps the refcount
      raw = dbus_message_new (name, dest_service);
      if (raw == IntPtr.Zero)
        throw new OutOfMemoryException ();
      dbus_message_unref (raw);
    }

    public string Name {
      get {
        return dbus_message_get_name (raw);
      }
    }

    public static Message Wrap (IntPtr ptr) {
      IntPtr gch_ptr;
      
      gch_ptr = dbus_message_get_data (ptr, wrapper_slot);
      if (gch_ptr != IntPtr.Zero) {
        return (DBus.Message) ((GCHandle)gch_ptr).Target;
      } else {
        return new Message (ptr);
      }
    }

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

          dbus_message_set_data (raw_, wrapper_slot,
                                 IntPtr.Zero, IntPtr.Zero);
          
          ((GCHandle) gch_ptr).Free ();
          
          dbus_message_unref (raw_);
        }
        
        raw_ = value;

        if (raw_ != IntPtr.Zero) {
          GCHandle gch;

          dbus_message_ref (raw_);

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

    ~Message () {
      raw = IntPtr.Zero; // free the native object
    }
    
    Message (IntPtr r) {
      raw = r;
    }
    
    // static constructor runs before any methods 
    static Message () {
      DBus.Internals.Init ();
      
      Debug.Assert (wrapper_slot == -1);
      
      if (!dbus_message_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.DBusLibname, EntryPoint="dbus_message_new")]
      private extern static IntPtr dbus_message_new (string name,
                                                     string dest_service);

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

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

    [DllImport (DBus.Internals.DBusLibname, EntryPoint="dbus_message_get_name")]
      private extern static string dbus_message_get_name (IntPtr ptr);

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

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

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

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